/* ═══════════════════════════════════════════════════════════════════
   THE SOCIAL SITE -- /network, /network/feed and /@username.
   ═══════════════════════════════════════════════════════════════════

   One stylesheet for the three pages of the Linxi Network, loaded AFTER
   css/style.css (which supplies the topbar, the post cards, the composer,
   the buttons and the theme tokens). Everything here is a token, so the
   light/dark choice made in the app (js/theme.js + /theme.css) holds on
   these pages too -- profile.html used to carry its own copy of every
   rule in hex, which is why it had no dark mode.

   A different vibe from the app on purpose: a brand-gradient cover band
   under the topbar, cards floating up into it, a wide three-column shell
   that uses the whole screen on desktop and goes edge to edge on a
   phone. The APP is dense and grey; this is a place people read. */

body.social-body { background: var(--c-page, #f4f5f7); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 0; color: var(--c-text, #0f172a); }
.social-body * { box-sizing: border-box; }

/* ── The site logo in the topbar ─────────────────────────────────────
   The same pair the app header carries: the light-theme logo and the
   dark-theme one, swapped by the theme rules in style.css
   (.header-logo-light / .header-logo-dark), with siteBrand.js dropping
   the dark one when no dark logo has been uploaded (.no-dark-logo). The
   id #header-logo-dark is what that fallback looks for, so it is the
   same id here as in the app -- one topbar per page. */
/* ⚠️ The topbar's own two layout rules moved to style.css, beside the rest
   of `.info-topbar-*`. They were scoped to `.social-body` and describe the
   BAR rather than the body it sits in, so the event page -- which is
   `.site-page-body` and does not load this stylesheet -- rendered the logo
   unsized and unaligned. A component's rules live with the component. */

/* ── The cover band ─────────────────────────────────────────────── */
.social-cover { background: linear-gradient(135deg, var(--c-brand, #6366f1) 0%, var(--c-brand-alt, #8b5cf6) 55%, var(--c-pink, #ec4899) 100%); color: var(--c-on-accent, #ffffff); position: relative; overflow: hidden; }
.social-cover::after { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse at 20% 0%, color-mix(in srgb, var(--c-highlight, #ffffff) 22%, transparent), transparent 55%), radial-gradient(ellipse at 90% 100%, color-mix(in srgb, var(--c-scrim, #000000) 18%, transparent), transparent 55%); pointer-events: none; }
.social-cover-inner { position: relative; z-index: 1; width: min(1440px, 100%); margin: 0 auto; padding: 2.25rem 2rem 4.25rem; display: flex; align-items: flex-end; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; }
.social-cover h1 { margin: 0; font-size: clamp(1.5rem, 2.6vw, 2.2rem); font-weight: 900; letter-spacing: -0.01em; display: flex; align-items: center; gap: 0.5rem; }
.social-cover p { margin: 0.35rem 0 0; font-size: 0.95rem; opacity: 0.9; max-width: 60ch; }
.social-cover-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.social-cover-actions a { background: color-mix(in srgb, var(--c-highlight, #ffffff) 18%, transparent); color: var(--c-on-accent, #ffffff); border: 1px solid color-mix(in srgb, var(--c-highlight, #ffffff) 35%, transparent); text-decoration: none; padding: 0.5rem 1rem; border-radius: 999px; font-weight: 700; font-size: 0.85rem; backdrop-filter: blur(6px); }
.social-cover-actions a:hover { background: color-mix(in srgb, var(--c-highlight, #ffffff) 30%, transparent); }
.social-cover-actions a.primary { background: var(--c-surface, #ffffff); color: var(--c-brand, #6366f1); border-color: transparent; }

/* ── The shell: full width, three rails on desktop ──────────────── */
.social-container { width: min(1440px, 100%); margin: 0 auto; padding: 0 2rem 3rem; }
.social-shell { display: grid; grid-template-columns: 300px minmax(0, 1fr) 320px; gap: 1.25rem; align-items: start; margin-top: -2.5rem; position: relative; z-index: 2; }
.social-shell-2 { grid-template-columns: 300px minmax(0, 1fr); }
.social-rail { display: flex; flex-direction: column; gap: 1rem; position: sticky; top: 4.25rem; }
.social-main { min-width: 0; }
.social-card { background: var(--c-surface, #ffffff); border-radius: 1rem; padding: 1.25rem; box-shadow: 0 1px 3px color-mix(in srgb, var(--c-shadow, #000000) 8%, transparent), 0 8px 24px color-mix(in srgb, var(--c-shadow, #000000) 5%, transparent); }
.social-card h3 { margin: 0 0 0.5rem; font-size: 0.95rem; font-weight: 800; }
.social-card + .social-card { margin-top: 0; }
.social-main .feed-toolbar { margin-top: 0; box-shadow: 0 1px 3px color-mix(in srgb, var(--c-shadow, #000000) 8%, transparent), 0 8px 24px color-mix(in srgb, var(--c-shadow, #000000) 5%, transparent); }
.social-main .compose-card, .social-main .post-card { box-shadow: 0 1px 3px color-mix(in srgb, var(--c-shadow, #000000) 8%, transparent), 0 8px 24px color-mix(in srgb, var(--c-shadow, #000000) 4%, transparent); }
.social-main .feed-toolbar h1, .social-main .feed-toolbar h2 { margin: 0; font-size: 1.05rem; font-weight: 800; }

/* Medium: the right rail drops under the left one, the main column keeps the width. */
@media (max-width: 1199px) {
  .social-shell { grid-template-columns: 280px minmax(0, 1fr); }
  .social-rail-left { grid-column: 1; grid-row: 1; }
  .social-rail-right { grid-column: 1; grid-row: 2; position: static; }
  .social-main { grid-column: 2; grid-row: 1 / span 2; }
}
/* Phone: one column, edge to edge, the cover shorter. */
@media (max-width: 899px) {
  .social-container { padding: 0 0.6rem 2.5rem; }
  .social-cover-inner { padding: 1.5rem 1rem 3rem; }
  .social-shell, .social-shell-2 { grid-template-columns: 1fr; gap: 0.85rem; margin-top: -2rem; }
  .social-rail { position: static; gap: 0.85rem; }
  .social-rail-left, .social-rail-right, .social-main { grid-column: 1; grid-row: auto; }
  .social-rail-right { order: 3; }
  .social-card, .social-main .post-card, .social-main .compose-card, .social-main .feed-toolbar { border-radius: 0.85rem; }
  .page-links-inner { padding-left: 1rem; padding-right: 1rem; }
}

/* ── The rails' contents (network.html) ─────────────────────────── */
.social-me-row { display: flex; gap: 0.75rem; align-items: center; }
.social-me-avatar { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; flex-shrink: 0; border: 3px solid var(--c-surface, #ffffff); box-shadow: 0 0 0 1px var(--c-border, #e2e8f0); }
.social-me-avatar-fallback { background: linear-gradient(135deg, var(--c-brand, #6366f1), var(--c-purple, #a855f7)); color: var(--c-on-accent, #ffffff); display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 1.25rem; }
.social-me-body { min-width: 0; }
.social-me-name { font-weight: 800; font-size: 1.05rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.social-me-handle { font-size: 0.82rem; color: var(--c-text-muted, #64748b); text-decoration: none; }
.social-me-stats { display: flex; gap: 1.25rem; margin-top: 0.85rem; }
.social-me-stat { text-align: left; cursor: pointer; }
.social-me-stat b { display: block; font-size: 1.05rem; font-weight: 800; }
.social-me-stat span { font-size: 0.72rem; color: var(--c-text-dim, #94a3b8); text-transform: uppercase; letter-spacing: 0.05em; }
.social-me-actions { display: flex; gap: 0.5rem; margin-top: 0.85rem; flex-wrap: wrap; }
.social-join h3 { font-size: 1.05rem; }
.social-join p { margin: 0 0 0.85rem; font-size: 0.85rem; color: var(--c-text-muted, #64748b); line-height: 1.5; }
.social-join-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.social-join-actions .compose-submit { text-decoration: none; display: inline-flex; align-items: center; }
.social-quiet { font-size: 0.8rem; color: var(--c-text-muted, #64748b); margin: 0; line-height: 1.5; }
.social-quiet a { color: var(--c-brand, #6366f1); text-decoration: none; font-weight: 600; }
.social-feed-note { margin: 0.6rem 0.25rem 0; }

.network-section-header { display: flex; align-items: center; justify-content: space-between; font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.07em; color: var(--c-text-dim, #94a3b8); margin-bottom: 0.6rem; }
.network-add-btn { background: var(--c-brand-tint, #ede9fe); border: none; color: var(--c-brand, #6366f1); cursor: pointer; font-size: 1rem; line-height: 1; width: 26px; height: 26px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; }
.network-add-btn:hover { background: var(--c-brand, #6366f1); color: var(--c-on-accent, #ffffff); }
.network-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.6rem; cursor: pointer; border-radius: 0.6rem; font-size: 0.85rem; color: var(--c-text-muted, #475569); }
.network-item:hover { background: var(--c-surface-sunken, #f1f5f9); color: var(--c-text, #0f172a); }
.network-item.active { background: var(--c-brand-tint, #ede9fe); color: var(--c-brand, #6366f1); font-weight: 600; }
.network-item-avatar { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; flex-shrink: 0; background: var(--c-border, #e2e8f0); display: flex; align-items: center; justify-content: center; font-size: 0.78rem; font-weight: 700; color: var(--c-text-muted, #64748b); }
.network-item-main { display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
.network-item-main span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.network-item-workspaces { font-size: 0.7rem; color: var(--c-text-dim, #94a3b8); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 0.1rem; }
.network-item-unread { background: var(--c-brand, #6366f1); color: var(--c-on-accent, #ffffff); font-size: 0.65rem; font-weight: 700; padding: 0.1rem 0.4rem; border-radius: 2rem; margin-left: auto; min-width: 18px; text-align: center; }
.network-item-pending { cursor: default; }
.network-item-pending:hover { background: none; color: var(--c-text-muted, #475569); }
.network-item-pending-note { font-size: 0.7rem; margin-left: auto; white-space: nowrap; }
.network-follow-back-btn { background: var(--c-brand, #6366f1); color: var(--c-on-accent, #ffffff); border: none; border-radius: 999px; padding: 0.35rem 0.7rem; font-size: 0.75rem; font-weight: 700; cursor: pointer; white-space: nowrap; }
.network-follow-back-btn:hover { background: var(--c-brand-strong, #4f46e5); }
.network-follow-back-btn:disabled { opacity: 0.6; cursor: default; }
.network-add-workspace-btn { background: var(--c-surface-sunken, #f1f5f9); color: var(--c-brand, #6366f1); }
.network-add-workspace-btn:hover { background: var(--c-brand-tint, #ede9fe); }
.network-follower-card { padding: 0.55rem 0.6rem; border-radius: 0.6rem; cursor: pointer; font-size: 0.85rem; color: var(--c-text-muted, #475569); }
.network-follower-card:hover { background: var(--c-surface-sunken, #f1f5f9); }
.network-follower-header { display: flex; align-items: center; gap: 0.6rem; }
.network-follower-actions { display: flex; gap: 0.4rem; margin-top: 0.5rem; padding-left: calc(32px + 0.6rem); flex-wrap: wrap; }
.network-follower-actions .network-follow-back-btn { flex: 1; text-align: center; }

/* A conversation in the main column. */
.social-conversation { padding: 1.25rem; }
.network-chat-header { display: flex; align-items: center; gap: 0.75rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--c-border, #e5e7eb); margin-bottom: 0.75rem; }
.network-back-link { font-size: 0.85rem; font-weight: 700; color: var(--c-brand, #6366f1); text-decoration: none; margin-right: 0.25rem; white-space: nowrap; }
.network-messages { overflow-y: auto; max-height: 60vh; display: flex; flex-direction: column; gap: 0.5rem; padding: 0.25rem 0; }
.network-message { display: flex; gap: 0.6rem; align-items: flex-start; }
.network-message.outbound { flex-direction: row-reverse; }
.network-message-bubble { max-width: 72%; padding: 0.6rem 0.9rem; border-radius: 1.1rem; font-size: 0.9rem; line-height: 1.5; }
.network-message.inbound .network-message-bubble { background: var(--c-surface-sunken, #f1f5f9); color: var(--c-text, #0f172a); border-bottom-left-radius: 0.3rem; }
.network-message.outbound .network-message-bubble { background: linear-gradient(135deg, var(--c-brand, #6366f1), var(--c-brand-alt, #8b5cf6)); color: var(--c-on-accent, #ffffff); border-bottom-right-radius: 0.3rem; }
.network-message-meta { font-size: 0.68rem; opacity: 0.75; margin-top: 0.2rem; }
.network-mention { color: var(--c-brand, #6366f1); font-weight: 600; }
.network-message.outbound .network-mention { color: inherit; text-decoration: underline; }
.network-input-area { margin-top: 0.75rem; }
.network-input-wrapper { position: relative; display: flex; gap: 0.5rem; align-items: flex-end; background: var(--c-surface-subtle, #f8fafc); border: 1px solid var(--c-border, #e2e8f0); border-radius: 1rem; padding: 0.5rem 0.75rem; }
.network-input { flex: 1; border: none; background: none; font-size: 0.9rem; resize: none; outline: none; max-height: 120px; min-height: 24px; font-family: inherit; color: var(--c-text, #0f172a); }
.network-input-wrapper .btn { border-radius: 999px; }
.mention-autocomplete { position: absolute; bottom: 100%; left: 0; right: 0; margin-bottom: 0.35rem; background: var(--c-surface, #ffffff); border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.6rem; box-shadow: 0 4px 15px color-mix(in srgb, var(--c-shadow, #000000) 12%, transparent); max-height: 200px; overflow-y: auto; z-index: 50; }
.mention-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.45rem 0.7rem; cursor: pointer; font-size: 0.85rem; }
.mention-item:hover { background: var(--c-brand-tint, #f5f3ff); }
.social-body .modal input[type="text"] { width: 100%; }
@media (max-width: 899px) { .network-messages { max-height: none; } .network-message-bubble { max-width: 85%; } }

/* ── The profile page (profile.html) ─────────────────────────────
   Its rules, moved out of the page's own <style> and tokenised. The
   page keeps its own cover (the user's, or the brand gradient) and its
   header card floats up into it, so it does not use .social-cover. */
.profile-container { width: min(1440px, 100%); max-width: none; margin: 0 auto; padding: 0 2rem 3rem; }
.profile-body { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 1.25rem; align-items: start; margin-top: 1.25rem; }
.profile-side, .profile-main { min-width: 0; }
.profile-side { position: sticky; top: 4.25rem; }
.profile-main .profile-tabs { margin-top: 0; }
.profile-side .post-card { margin-top: 0; }
.profile-cover { height: 240px; }
@media (max-width: 899px) {
  .profile-container { padding: 0 0.6rem 2.5rem; }
  .profile-body { grid-template-columns: 1fr; gap: 0.85rem; }
  .profile-side { position: static; }
  .pe-grid, .lead-ws-list { grid-template-columns: 1fr; }
  .profile-cover { height: 160px; }
}
  .profile-main .profile-tabs { margin-top: 0; }
  .profile-side .post-card { margin-top: 0; }
  .plinks-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.6rem; }
  .plinks-head h3 { font-size: 0.95rem; font-weight: 700; margin: 0; }
  .plinks { display: flex; flex-direction: column; gap: 0.5rem; }
  /* A link is a PILL: the page's own image as a round badge, its title,
     the domain in small under it. Full-width so the titles line up. */
  .plink { display: flex; gap: 0.65rem; align-items: center; padding: 0.4rem 0.9rem 0.4rem 0.4rem; border-radius: 999px; border: 1px solid var(--c-border, #e2e8f0); text-decoration: none; color: inherit; background: linear-gradient(135deg, var(--c-surface-subtle, #f8fafc), var(--c-surface, #ffffff)); box-shadow: 0 1px 2px color-mix(in srgb, var(--c-shadow, #000000) 5%, transparent); transition: transform 0.12s ease, box-shadow 0.12s ease; }
  a.plink:hover { transform: translateY(-1px); box-shadow: 0 4px 12px color-mix(in srgb, var(--c-brand, #6366f1) 15%, transparent); border-color: var(--c-brand-border, #c7d2fe); }
  .plink-img { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; background: linear-gradient(135deg, var(--c-brand-tint-2, #e0e7ff), var(--c-brand-tint, #fce7f3)); flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 1rem; color: var(--c-brand, #6366f1); border: 2px solid var(--c-surface, #ffffff); box-shadow: 0 0 0 1px var(--c-border, #e2e8f0); }
  .plink-body { min-width: 0; flex: 1; line-height: 1.25; }
  .plink-title { font-weight: 700; font-size: 0.85rem; color: var(--c-text, #0f172a); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .plink-desc { font-size: 0.72rem; color: var(--c-text-muted, #64748b); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .plink-domain { font-size: 0.7rem; color: var(--c-text-dim, #94a3b8); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .plink-tools { display: flex; gap: 0.15rem; flex-shrink: 0; }
  .plink-tools button { background: var(--c-surface-sunken, #f1f5f9); border: none; border-radius: 0.4rem; width: 26px; height: 26px; cursor: pointer; font-size: 0.75rem; color: var(--c-text-muted, #475569); }
  .plink-tools button:hover { background: var(--c-border, #e2e8f0); }
  .plinks-add { display: flex; gap: 0.4rem; margin-top: 0.75rem; }
  .plinks-add input { flex: 1; min-width: 0; padding: 0.5rem 0.6rem; border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.5rem; font-size: 0.8rem; font-family: inherit; }
  .plinks-add button { background: var(--c-brand, #6366f1); color: var(--c-on-accent, #ffffff); border: none; border-radius: 0.5rem; padding: 0 0.8rem; font-weight: 600; font-size: 0.8rem; cursor: pointer; }
  .plinks-note { font-size: 0.72rem; color: var(--c-text-dim, #94a3b8); margin: 0.5rem 0 0; }
  .plinks-empty { font-size: 0.8rem; color: var(--c-text-dim, #94a3b8); margin: 0; }

  /* The editor is ON the page, in the main column, above the feed. */
  .profile-editor { background: var(--c-surface, #ffffff); border-radius: 1rem; padding: 1.25rem; box-shadow: 0 1px 3px color-mix(in srgb, var(--c-shadow, #000000) 8%, transparent); margin-bottom: 1rem; }
  .profile-editor h3 { margin: 0 0 1rem; font-weight: 700; font-size: 1rem; }
  .profile-editor label { font-size: 0.78rem; font-weight: 600; color: var(--c-text-muted, #475569); display: block; margin-bottom: 0.3rem; }
  .profile-editor input[type="text"], .profile-editor textarea, .profile-editor select { width: 100%; padding: 0.6rem; border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.5rem; font-size: 0.875rem; margin-bottom: 0.85rem; font-family: inherit; }
  .pe-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1rem; }
  .pe-check { display: flex; gap: 0.6rem; align-items: flex-start; font-size: 0.85rem; font-weight: 500; color: var(--c-text, #0f172a); margin-bottom: 0.6rem; cursor: pointer; }
  .pe-check input { margin-top: 0.2rem; }
  .pe-check small { display: block; font-size: 0.74rem; color: var(--c-text-muted, #64748b); font-weight: 400; margin-top: 0.15rem; }
  .pe-section { border-top: 1px solid var(--c-surface-sunken, #f1f5f9); padding-top: 0.85rem; margin-top: 0.25rem; }
  .pe-actions { display: flex; gap: 0.6rem; align-items: center; margin-top: 0.5rem; }
  .pe-actions .compose-submit { flex: 1; }
  .lead-ws-list { display: grid; grid-template-columns: 1fr 1fr; gap: 0.35rem 1rem; margin-bottom: 0.85rem; }

  .profile-cover {
    width: 100%; height: 200px;
    background: linear-gradient(135deg, var(--c-brand, #6366f1), var(--c-brand-alt, #8b5cf6), var(--c-pink, #ec4899));
    border-radius: 0 0 1rem 1rem;
    overflow: hidden; position: relative; cursor: default;
  }
  .profile-cover img { width: 100%; height: 100%; object-fit: cover; }
  /* ⚠️⚠️ TOP RIGHT, AND THE MOVE IS WHY THERE IS NO z-index HERE ANY MORE.
     At the BOTTOM this button was painted over: .profile-header below
     carries `margin-top: -2rem` with `position: relative` and no z-index,
     so it is pulled 32px up over the bottom of the cover and -- later in
     the DOM at the same stacking level -- paints on top of anything
     sitting in that strip. A z-index of 1 was holding the button above
     it. Nothing overlaps the cover's TOP edge, so that prop has been
     removed rather than left behind as cargo: a stacking value with no
     remaining reason is the kind nobody dares touch later.

     ⚠️ THE STICKY TOPBAR IS NOT A PROBLEM AND MUST NOT BE FOUGHT. It is
     z-index 100, so it correctly covers this button as the page scrolls
     past -- the cover is scrolling away underneath it. Raising the button
     to beat it would leave an Edit Cover control floating over the site
     nav. */
  .profile-cover-edit {
    position: absolute; top: 0.75rem; right: 0.75rem;
    background: color-mix(in srgb, var(--c-scrim, #000000) 55%, transparent); color: var(--c-on-accent, #ffffff); border: none;
    padding: 0.4rem 0.75rem; border-radius: 0.5rem; font-size: 0.78rem; cursor: pointer;
  }

  .profile-header {
    background: var(--c-surface, #ffffff); border-radius: 1rem; padding: 0 1.25rem 1.25rem;
    margin-top: -2rem; position: relative; box-shadow: 0 1px 3px color-mix(in srgb, var(--c-shadow, #000000) 8%, transparent);
  }
  .profile-avatar-wrap { position: relative; display: inline-block; margin-top: -40px; padding-top: 1rem; }
  .profile-avatar {
    width: 80px; height: 80px; border-radius: 50%; border: 4px solid var(--c-surface, #ffffff);
    object-fit: cover; background: var(--c-border, #e2e8f0); display: flex; align-items: center;
    justify-content: center; font-size: 2rem; font-weight: 700; color: var(--c-text-muted, #64748b);
  }
  .profile-avatar-edit {
    position: absolute; bottom: 2px; right: 2px; width: 26px; height: 26px;
    background: var(--c-brand, #6366f1); color: var(--c-on-accent, #ffffff); border: none; border-radius: 50%;
    font-size: 0.7rem; cursor: pointer; display: flex; align-items: center; justify-content: center;
  }
  .profile-name { font-size: 1.25rem; font-weight: 800; color: var(--c-text, #0f172a); }
  .profile-username { color: var(--c-text-dim, #94a3b8); font-size: 0.875rem; }
  .profile-bio { color: var(--c-text-muted, #475569); font-size: 0.875rem; margin-top: 0.5rem; line-height: 1.6; }
  .profile-meta { display: flex; gap: 1.25rem; margin-top: 0.5rem; font-size: 0.8rem; color: var(--c-text-dim, #94a3b8); flex-wrap: wrap; }
  .profile-meta-item { display: flex; align-items: center; gap: 0.3rem; }
  .profile-stats { display: flex; gap: 1.5rem; margin-top: 0.75rem; }
  .profile-stat { cursor: pointer; text-align: center; }
  .profile-stat-num { font-weight: 800; font-size: 1.1rem; color: var(--c-text, #0f172a); display: block; }
  .profile-stat-label { font-size: 0.75rem; color: var(--c-text-dim, #94a3b8); }
  .profile-actions { display: flex; gap: 0.5rem; margin-top: 1rem; flex-wrap: wrap; }

  .btn-follow {
    background: linear-gradient(135deg, var(--c-brand, #6366f1), var(--c-brand-alt, #8b5cf6)); color: var(--c-on-accent, #ffffff); border: none;
    padding: 0.5rem 1.5rem; border-radius: 2rem; font-weight: 700; font-size: 0.875rem; cursor: pointer;
  }
  .btn-follow.following { background: var(--c-surface, #ffffff); color: var(--c-brand, #6366f1); border: 1px solid var(--c-brand, #6366f1); }
  /* ⚠⚠ `gap` IS LOAD-BEARING HERE, NOT DECORATION. This is an
     inline-FLEX box, and the network rail's button is written
     `Open <span data-site-brand>...</span>` -- a bare text node beside an
     element. Flexbox makes that text node an anonymous flex item and
     STRIPS the whitespace at its edges, so the space between "Open" and
     the site's name did not exist at render time and the button read
     "OpenSensorium Events". The space is in the markup and always was;
     nothing but a gap can put it back, because there is no text node left
     to hold it. */
  .btn-secondary-pill {
    background: var(--c-surface-sunken, #f1f5f9); color: var(--c-text-muted, #475569); border: none; padding: 0.5rem 1.25rem;
    border-radius: 2rem; font-weight: 600; font-size: 0.875rem; cursor: pointer; text-decoration: none;
    display: inline-flex; align-items: center; gap: 0.35em;
  }

  .profile-tabs {
    display: flex; background: var(--c-surface, #ffffff); border-radius: 1rem; margin-top: 1rem;
    overflow: hidden; box-shadow: 0 1px 3px color-mix(in srgb, var(--c-shadow, #000000) 8%, transparent);
  }
  .profile-tab {
    flex: 1; padding: 0.85rem; text-align: center; font-size: 0.875rem; font-weight: 600;
    color: var(--c-text-dim, #94a3b8); cursor: pointer; border: none; background: none;
    border-bottom: 2px solid transparent;
  }
  .profile-tab.active { color: var(--c-brand, #6366f1); border-bottom-color: var(--c-brand, #6366f1); }

  .post-card { background: var(--c-surface, #ffffff); border-radius: 1rem; padding: 1.25rem; margin-top: 0.75rem; box-shadow: 0 1px 3px color-mix(in srgb, var(--c-shadow, #000000) 6%, transparent); }
  .post-header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; }
  .post-avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; background: var(--c-border, #e2e8f0); flex-shrink: 0; }
  .post-meta { flex: 1; min-width: 0; }
  .post-author { font-weight: 700; font-size: 0.9rem; color: var(--c-text, #0f172a); text-decoration: none; }
  .post-time { font-size: 0.75rem; color: var(--c-text-dim, #94a3b8); }
  .post-body { font-size: 0.9rem; color: var(--c-text, #0f172a); line-height: 1.7; margin-bottom: 0.75rem; white-space: pre-wrap; word-break: break-word; }
  .post-body a { color: var(--c-brand, #6366f1); text-decoration: none; }
  .post-image { width: 100%; max-height: 400px; object-fit: cover; border-radius: 0.75rem; margin-bottom: 0.75rem; cursor: pointer; }
  .post-link-preview { border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.75rem; overflow: hidden; margin-bottom: 0.75rem; text-decoration: none; display: block; }
  .post-link-image { width: 100%; height: 160px; object-fit: cover; }
  .post-link-info { padding: 0.75rem; }
  .post-link-title { font-weight: 700; font-size: 0.875rem; color: var(--c-text, #0f172a); margin-bottom: 0.25rem; }
  .post-link-desc { font-size: 0.78rem; color: var(--c-text-muted, #64748b); line-height: 1.5; }
  .post-link-domain { font-size: 0.72rem; color: var(--c-text-dim, #94a3b8); margin-top: 0.35rem; }
  .post-repost-attribution { font-size: 0.78rem; color: var(--c-text-dim, #94a3b8); margin-bottom: 0.5rem; }
  .post-repost-original { border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.75rem; padding: 0.85rem; margin-bottom: 0.75rem; }

  .post-reactions { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
  .reaction-btn { display: inline-flex; align-items: center; gap: 0.25rem; padding: 0.3rem 0.6rem; border-radius: 2rem; border: 1px solid var(--c-border, #e2e8f0); background: var(--c-surface, #ffffff); cursor: pointer; font-size: 0.8rem; }
  .reaction-btn.active { background: var(--c-brand-tint, #f5f3ff); border-color: var(--c-brand-border, #c4b5fd); }
  .reaction-btn .reaction-count { font-weight: 700; color: var(--c-brand, #6366f1); }
  .reaction-more-btn { padding: 0.3rem 0.6rem; border-radius: 2rem; border: 1px solid var(--c-border, #e2e8f0); background: var(--c-surface, #ffffff); cursor: pointer; font-size: 0.78rem; color: var(--c-text-dim, #94a3b8); }

  .post-actions { display: flex; gap: 0.5rem; padding-top: 0.75rem; border-top: 1px solid var(--c-surface-sunken, #f1f5f9); }
  .post-action-btn { display: flex; align-items: center; gap: 0.35rem; padding: 0.4rem 0.75rem; border-radius: 0.5rem; border: none; background: none; cursor: pointer; font-size: 0.8rem; color: var(--c-text-dim, #94a3b8); font-weight: 600; }

  .post-comments { margin-top: 0.75rem; border-top: 1px solid var(--c-surface-sunken, #f1f5f9); padding-top: 0.75rem; }
  .comment-item { display: flex; gap: 0.5rem; margin-bottom: 0.75rem; }
  .comment-avatar { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
  .comment-bubble { background: var(--c-surface-subtle, #f8fafc); border-radius: 0.75rem; padding: 0.5rem 0.75rem; flex: 1; }
  .comment-author { font-weight: 700; font-size: 0.78rem; color: var(--c-text, #0f172a); }
  .comment-body { font-size: 0.82rem; color: var(--c-text-muted, #475569); line-height: 1.5; margin-top: 0.15rem; }
  .comment-time { font-size: 0.7rem; color: var(--c-text-dim, #94a3b8); margin-top: 0.25rem; }
  .comment-input-wrap { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.5rem; }
  .comment-input { flex: 1; border: 1px solid var(--c-border, #e2e8f0); border-radius: 2rem; padding: 0.5rem 0.85rem; font-size: 0.82rem; outline: none; }
  .comment-submit { background: var(--c-brand, #6366f1); color: var(--c-on-accent, #ffffff); border: none; padding: 0.5rem 0.85rem; border-radius: 2rem; font-size: 0.78rem; font-weight: 600; cursor: pointer; }

  .compose-card { background: var(--c-surface, #ffffff); border-radius: 1rem; padding: 1rem; margin-top: 0.75rem; box-shadow: 0 1px 3px color-mix(in srgb, var(--c-shadow, #000000) 6%, transparent); }
  .compose-input { width: 100%; border: none; outline: none; font-size: 0.95rem; resize: none; font-family: inherit; color: var(--c-text, #0f172a); min-height: 60px; background: transparent; }
  .compose-toolbar { display: flex; align-items: center; gap: 0.5rem; padding-top: 0.75rem; border-top: 1px solid var(--c-surface-sunken, #f1f5f9); margin-top: 0.5rem; }
  .compose-tool-btn { background: none; border: none; padding: 0.4rem; cursor: pointer; color: var(--c-text-dim, #94a3b8); border-radius: 0.35rem; font-size: 1rem; }
  .compose-visibility { margin-left: auto; }
  .compose-visibility select { border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.5rem; padding: 0.35rem 0.6rem; font-size: 0.78rem; color: var(--c-text-muted, #475569); background: var(--c-surface, #ffffff); cursor: pointer; }
  .compose-submit { background: linear-gradient(135deg, var(--c-brand, #6366f1), var(--c-brand-alt, #8b5cf6)); color: var(--c-on-accent, #ffffff); border: none; padding: 0.5rem 1.25rem; border-radius: 2rem; font-weight: 700; font-size: 0.875rem; cursor: pointer; }
  .compose-submit:disabled { opacity: 0.5; cursor: not-allowed; }
  .compose-image-preview { width: 100%; max-height: 200px; object-fit: cover; border-radius: 0.75rem; margin-top: 0.5rem; display: none; }
  .compose-link-preview { border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.75rem; overflow: hidden; margin-top: 0.5rem; display: none; }

  .private-overlay { position: fixed; inset: 0; background: color-mix(in srgb, var(--c-surface, #ffffff) 97%, transparent); display: flex; align-items: center; justify-content: center; z-index: 50; }
  .private-overlay-content { text-align: center; max-width: 400px; padding: 2rem; }

  .emoji-picker-overlay, .app-modal-overlay { position: fixed; inset: 0; z-index: 10000; background: color-mix(in srgb, var(--c-scrim, #000000) 45%, transparent); display: flex; align-items: center; justify-content: center; padding: 1rem; }
  .emoji-picker { background: var(--c-surface, #ffffff); border-radius: 1rem; padding: 1rem; width: 320px; max-height: 400px; overflow-y: auto; box-shadow: 0 20px 60px color-mix(in srgb, var(--c-shadow, #000000) 20%, transparent); }
  /* Twice the old 320px on desktop; the phone rule below still wins there. */
  .app-modal { background: var(--c-surface, #ffffff); border-radius: 1rem; padding: 1.25rem; width: min(640px, 92vw); max-height: 80vh; overflow-y: auto; box-shadow: 0 20px 60px color-mix(in srgb, var(--c-shadow, #000000) 20%, transparent); }
  .emoji-picker-search { width: 100%; padding: 0.5rem 0.75rem; border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.5rem; font-size: 0.875rem; margin-bottom: 0.75rem; outline: none; }
  .emoji-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 0.25rem; }
  .emoji-btn { background: none; border: none; font-size: 1.25rem; padding: 0.35rem; cursor: pointer; border-radius: 0.35rem; text-align: center; }
  .emoji-btn:hover { background: var(--c-brand-tint, #f5f3ff); }

  .reactions-modal { background: var(--c-surface, #ffffff); border-radius: 1rem; padding: 1.25rem; width: 360px; max-height: 500px; overflow-y: auto; box-shadow: 0 20px 60px color-mix(in srgb, var(--c-shadow, #000000) 20%, transparent); }
  .reaction-user-item { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0; border-bottom: 1px solid var(--c-surface-sunken, #f1f5f9); }
  .reaction-user-avatar { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; }

  .app-modal label { font-size: 0.78rem; font-weight: 600; color: var(--c-text-muted, #475569); display: block; margin-bottom: 0.3rem; }
  .app-modal input, .app-modal textarea, .app-modal select { width: 100%; padding: 0.6rem; border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.5rem; font-size: 0.875rem; margin-bottom: 0.85rem; font-family: inherit; }

  @media (max-width: 640px) {
    .profile-stats { gap: 1rem; }
    .emoji-grid { grid-template-columns: repeat(6, 1fr); }
    .reactions-modal, .emoji-picker, .app-modal { width: 90vw; }
  }

/* ── One post's page (src/services/postPage.js) ──────────────────── */
.post-page-cover { height: 200px; }
.post-page-author { display: flex; gap: 1rem; align-items: flex-start; padding-top: 0; }
.post-page-author-body { min-width: 0; flex: 1; padding-top: 1rem; }
.post-page-author .profile-name a { color: inherit; text-decoration: none; }
.post-page-body { max-width: 760px; margin: 1.25rem auto 0; }
.post-page-visibility { margin: 0 0 0.5rem; }
.post-page-more { margin: 1rem 0.25rem 0; }
.post-static .post-handle { color: var(--c-text-dim, #94a3b8); font-size: 0.78rem; margin-left: 0.25rem; }
.post-static .post-body a { color: var(--c-brand, #6366f1); text-decoration: none; }
.post-static .post-gallery { display: grid; gap: 0.35rem; margin-bottom: 0.75rem; }
.post-static .post-gallery img { width: 100%; border-radius: 0.6rem; object-fit: cover; }
.post-static .post-gallery-1 { grid-template-columns: 1fr; }
.post-static .post-gallery-2, .post-static .post-gallery-4 { grid-template-columns: 1fr 1fr; }
.post-static .post-gallery-3 { grid-template-columns: 1fr 1fr 1fr; }
.post-static-note { margin: 0.5rem 0 0; }
.post-view-link { text-decoration: none; margin-left: auto; }
@media (max-width: 899px) {
  .post-page-cover { height: 140px; }
  .post-page-author { flex-direction: column; gap: 0; }
}

/* ── The site calendar (public/calendar.html) ─────────────────────── */
/*
 * ⚠️ THE GRID ITSELF IS style.css's `.cal-*`, reused rather than copied:
 * this page loads that stylesheet, and a second month grid would be a
 * second set of rules to keep in step for no gain. Only what is specific
 * to the PUBLIC page is here -- the chips in a square and the list rows,
 * which link out to an event page rather than opening an editor.
 */
.sitecal-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; flex-wrap: wrap; margin-bottom: 0.75rem;
}
.sitecal-card .cal-grid-wrap { margin: 0; }

/* The head's right-hand controls. `margin-left: auto` rather than leaning
   on the flex `space-between`, because in LIST VIEW the month nav is
   hidden and this is the only child left -- with space-between alone it
   would jump to the left edge as the toggle was pressed. */
.sitecal-head-actions {
  display: flex; align-items: center; gap: 0.5rem;
  flex-wrap: wrap; margin-left: auto;
}

/* ⚠️ The truncation note, and it is MUTED rather than a warning colour:
   "there are more further out" is a fact about the list's length, not a
   problem anybody has to solve. */
.sitecal-capped { margin: 0.75rem 0 0; font-size: 0.78rem; }

/* ⚠️⚠️ AN AUTHOR `display` RULE BEATS THE UA STYLESHEET'S
   [hidden]{display:none} AT ANY SPECIFICITY, and that is what shipped:
   the list view set `hidden` on all three month controls, the grid
   vanished, and `‹ September 2026 ›` and `Today` stayed on screen -- so
   the page opened looking like a month view with a list under it, and
   the two buttons that were still there did nothing anybody could see.

     .cal-grid-wrap   no display rule     -> hidden worked
     .cal-nav         display: inline-flex -> hidden DEFEATED
     button           display: inline-flex -> hidden DEFEATED

   Eighth instance in this codebase -- .myai-save-bar, .email-ed,
   .email-merge-panel, .email-preview-inline, .composer-actions,
   .send-countdown, .send-test-panel and .campaign-rate-row each carry
   the same two words beside them.

   ⚠️ SCOPED TO THE CARD RATHER THAN NAMED ONE BY ONE, so a control added
   to this head later is covered without anybody remembering. (0,2,0)
   beats `.cal-nav` and `button` on specificity, so it does not depend on
   which stylesheet loads last. */
.sitecal-card [hidden] { display: none; }

/* ⚠️ In list view the grid card keeps only its head bar, so the margin
   under it would leave a gap with nothing above it. Collapsed when the
   grid is away, by asking whether the grid is hidden rather than by a
   class on the body -- one fact, read where it is true. */
.sitecal-head:has(+ #sitecal-gridwrap[hidden]) { margin-bottom: 0; }

.sitecal-chip {
  display: block; font-size: 0.7rem; line-height: 1.3;
  padding: 0.1rem 0.3rem; margin-top: 0.15rem; border-radius: 0.3rem;
  background: var(--c-brand-tint, #f5f3ff); color: var(--c-brand-deep, #4338ca);
  text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sitecal-chip:hover { background: var(--c-violet-tint-2, #ede9fe); }
/* ⚠️ The star carries the meaning as well as the colour -- "you are
   going" must not be colour alone, which is invisible in forced colours
   and to a good number of readers. Same rule as the SMS Available pill. */
.sitecal-chip.is-mine {
  background: var(--c-success-tint, #dcfce7); color: var(--c-success-text, #166534);
  font-weight: 600;
}
.sitecal-chip.is-off { text-decoration: line-through; opacity: 0.7; }

.sitecal-row {
  display: flex; gap: 0.85rem; align-items: flex-start;
  padding: 0.7rem 0.2rem; text-decoration: none; color: inherit;
  border-bottom: 1px solid var(--c-border, #e2e8f0);
}
.sitecal-row:last-child { border-bottom: none; }
.sitecal-row:hover { background: var(--c-surface-subtle, #f8fafc); }
.sitecal-row-when {
  flex: 0 0 8.5rem; font-size: 0.78rem; font-weight: 600;
  color: var(--c-text-muted, #64748b);
}
.sitecal-row-main { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.sitecal-row.is-off .sitecal-row-main strong { text-decoration: line-through; }

.sitecal-tag {
  display: inline-block; align-self: flex-start;
  font-size: 0.68rem; font-weight: 600; padding: 0.05rem 0.4rem;
  border-radius: 0.6rem; margin-top: 0.1rem;
}
.sitecal-tag.is-mine { background: var(--c-success-tint, #dcfce7); color: var(--c-success-text, #166534); }
.sitecal-tag.is-off { background: var(--c-danger-tint, #fee2e2); color: var(--c-danger, #dc2626); }

@media (max-width: 560px) {
  /* ⚠️⚠️ THE CHIPS BECOME DOTS. THEY USED TO BECOME NOTHING.
     A 7-column grid at phone width gives each square about 44px, which
     cannot hold a title -- so this was `display: none`, and every square
     in the month then looked exactly alike. The grid could no longer
     answer the one question it is FOR, which is which days have something
     on them; "the list underneath names them" was true and did not help
     anybody looking at the grid.

     44px cannot hold a title. It can hold a MARK. The list still names
     them, and siteCalendar.js opens a phone on that list -- this is what
     the reader sees when they go back to the month. */
  .sitecal-card .cal-cell {
    min-height: 3rem;
    /* Row-wrap so the marks flow along under the date rather than
       stacking one per line and pushing the square open. */
    flex-flow: row wrap;
    align-content: flex-start;
    gap: 3px;
    padding: 0.25rem 0.2rem;
  }
  /* ⚠️⚠️ A ZERO-HEIGHT SPACER IS WHAT BREAKS THE LINE, not
     `flex: 0 0 100%` on the date. The date is ALSO the today marker -- a
     filled circle 1.35rem across -- and stretching it to the width of the
     square turns that circle into a bar right across the cell. So it keeps
     its natural size, and an ::before ordered BETWEEN it and the marks
     takes a whole line and no height. */
  .sitecal-card .cal-cell::before {
    content: ''; order: 1; flex: 0 0 100%; height: 0;
  }
  .sitecal-card .cal-day-num { order: 0; }
  /* ⚠️ `font-size: 0` RATHER THAN REMOVING THE TEXT. The chip is still
     a link to the event and still carries its title, so a screen reader
     reads exactly what it read before; only the sighted presentation
     collapses. Dropping the text would have made the grid emptier for a
     reader who cannot see the dot in the first place. */
  .sitecal-chip {
    order: 2;
    display: flex; align-items: center; justify-content: center;
    flex: 0 0 auto; width: 8px; height: 8px;
    margin: 0; padding: 0; border-radius: 50%;
    font-size: 0; line-height: 0; overflow: hidden;
    background: var(--c-brand, #6366f1);
  }
  /* ⚠️⚠️ THE STAR SURVIVES THE COLLAPSE, AND THAT IS THE POINT.
     "You are going" must not be carried by colour alone -- the rule the
     full-size chip already follows, and the reason it has a ★ beside the
     title at all. Two dots that differ only in hue would have quietly
     dropped that on the one screen where the label is gone too. So the
     mark for `is-mine` is a different SHAPE: the star itself, at the size
     the dot would have been. */
  .sitecal-chip.is-mine {
    width: 10px; height: 10px;
    background: none; overflow: visible;
    color: var(--c-success-text, #166534);
  }
  .sitecal-chip.is-mine > span { font-size: 10px; line-height: 1; }
  /* Cancelled is a shape too: a hollow ring. A line through an 8px dot
     is a line through nothing. */
  .sitecal-chip.is-off {
    width: 9px; height: 9px;
    background: none; opacity: 1;
    border: 1.5px solid var(--c-text-dim, #94a3b8);
    text-decoration: none;
  }
  /* ⚠️ SCOPED, because `.cal-more` is the APP calendar's class too and
     an unscoped rule here would reach a grid this file is not about.
     "+2 more" is a phrase in a 44px box; the marks already say there is
     more than one, and the list gives the count. */
  .sitecal-card .cal-more { display: none; }

  .sitecal-row { flex-direction: column; gap: 0.2rem; }
  .sitecal-row-when { flex: none; }
}

/* ── Linxi Cloud ─────────────────────────────────────────────────────
   ⚠️ EVERY TOKEN CARRIES A FALLBACK. A bare var(--x) on a token nothing
   declares is invalid at computed-value time and does NOTHING, silently
   -- CLAUDE.md lists five that shipped that way. The fallback is what
   makes these render on a deployment whose theme has not set them. */
.cloud-panel { display: flex; flex-direction: column; gap: 0.9rem; }
.cloud-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.cloud-head h3 { font-size: 0.95rem; font-weight: 700; margin: 0; }
.cloud-actions { display: flex; gap: 0.5rem; }

.cloud-quota { display: flex; flex-direction: column; gap: 0.3rem; }
.cloud-quota-bar { height: 0.4rem; border-radius: 0.3rem; overflow: hidden; background: var(--c-surface-2, #e2e8f0); }
.cloud-quota-bar span { display: block; height: 100%; background: var(--c-accent, #6366f1); }
.cloud-quota small { color: var(--c-text-dim, #94a3b8); font-size: 0.75rem; }

.cloud-crumbs { display: flex; align-items: center; flex-wrap: wrap; gap: 0.25rem; font-size: 0.8rem; }
.cloud-crumb { background: none; border: none; padding: 0.15rem 0.25rem; cursor: pointer; color: var(--c-accent, #6366f1); border-radius: 0.3rem; }
.cloud-crumb-sep { color: var(--c-text-dim, #94a3b8); }

.cloud-drop {
  border: 1px dashed var(--c-border, #cbd5e1); border-radius: 0.6rem;
  padding: 1rem; text-align: center; font-size: 0.8rem;
  color: var(--c-text-dim, #94a3b8); cursor: pointer;
}
.cloud-drop-over { border-color: var(--c-accent, #6366f1); background: var(--c-surface-2, #f1f5f9); }

.cloud-grid { display: grid; gap: 0.6rem; }
.cloud-grid-folders { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.cloud-grid-files { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }

.cloud-folder, .cloud-file {
  display: flex; flex-direction: column; gap: 0.2rem; text-align: left;
  padding: 0.6rem; border-radius: 0.6rem; cursor: pointer;
  border: 1px solid var(--c-border, #e2e8f0);
  background: var(--c-surface, #fff); color: var(--c-text, #0f172a);
}
.cloud-folder:hover, .cloud-file:hover { border-color: var(--c-accent, #6366f1); }
/* ⚠️ THE FOLDER ICON IS A SQUARE TILE, matching .cloud-thumb below rather
   than sitting as a stray glyph above the name. Folders and files appear
   in the same view, one grid under the other, and a 1.4rem emoji beside a
   full-width square thumbnail made the two read as different components
   rather than two kinds of the same thing.

   aspect-ratio + place-items does the centring; line-height:1 stops the
   emoji's own font metrics adding a lopsided gap under it, which is what
   makes a large emoji look badly centred rather than actually being so. */
.cloud-folder-icon {
  width: 100%;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  line-height: 1;
  font-size: 3rem;
  border-radius: 0.4rem;
  background: var(--c-surface-2, #f1f5f9);
}
.cloud-folder-name, .cloud-file-name {
  font-size: 0.8rem; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cloud-folder-meta, .cloud-file-meta { font-size: 0.7rem; color: var(--c-text-dim, #94a3b8); }

.cloud-thumb { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 0.4rem; background: var(--c-surface-2, #f1f5f9); }
/* A video has no poster frame on this install, so it gets a mark rather
   than a broken <img> -- unknown must not render as damaged. */
.cloud-thumb-none { display: grid; place-items: center; font-size: 1.6rem; }

.cloud-status { font-size: 0.75rem; color: var(--c-text-dim, #94a3b8); min-height: 1rem; margin: 0; }
.cloud-status-bad { color: var(--c-danger, #dc2626); }
.cloud-empty-line { font-size: 0.8rem; color: var(--c-text-dim, #94a3b8); }
.cloud-loading { padding: 1.5rem; text-align: center; color: var(--c-text-dim, #94a3b8); font-size: 0.85rem; }

/* ⚠️ The .modal sits INSIDE .modal-overlay and carries no positioning of
   its own; the overlay is the band at z-index 1300. */
.cloud-picker { width: min(760px, 94vw); max-height: 86vh; overflow: auto; padding: 1rem; }
.cloud-picker-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem; }
.cloud-picker-head h3 { margin: 0; font-size: 0.95rem; font-weight: 700; }
.cloud-picker-close { background: none; border: none; font-size: 1.4rem; line-height: 1; cursor: pointer; color: var(--c-text-dim, #94a3b8); }

.compose-cloud-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.4rem; }
.compose-cloud-chip {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-size: 0.72rem; padding: 0.15rem 0.45rem; border-radius: 0.8rem;
  background: var(--c-surface-2, #f1f5f9); color: var(--c-text-3, #475569);
}
.compose-cloud-drop { background: none; border: none; cursor: pointer; color: var(--c-text-dim, #94a3b8); font-size: 0.85rem; line-height: 1; padding: 0; }

/* The folder name + privacy dialog. Native to the site rather than
   window.prompt, which cannot carry a second field or explain what each
   privacy setting means. */
.cloud-dialog { width: min(440px, 94vw); padding: 1.25rem; }
.cloud-dialog h3 { margin: 0 0 0.9rem; font-size: 1rem; font-weight: 700; }
.cloud-dialog-label { font-size: 0.78rem; font-weight: 600; margin: 0 0 0.3rem; color: var(--c-text-3, #475569); }
.cloud-dialog-input {
  width: 100%; padding: 0.55rem 0.7rem; border-radius: 0.5rem;
  border: 1px solid var(--c-border, #cbd5e1);
  background: var(--c-surface, #fff); color: var(--c-text, #0f172a);
  font-size: 0.9rem; margin-bottom: 1rem;
}
.cloud-dialog-input:focus { outline: 2px solid var(--c-accent, #6366f1); outline-offset: 1px; }

.cloud-vis-list { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 0.75rem; }
/* ⚠️ A GRID, NOT A FLEX ROW. The radio sits in column one and both lines
   of text share column two, so the note aligns under the label instead of
   under the radio. A flex row would also eat the whitespace between the
   mark and the label. */
.cloud-vis {
  display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 0.6rem;
  align-items: start; padding: 0.55rem 0.65rem; cursor: pointer;
  border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.55rem;
}
.cloud-vis:hover { border-color: var(--c-accent, #6366f1); }
.cloud-vis input { grid-row: 1 / span 2; margin-top: 0.2rem; }
.cloud-vis-main { font-size: 0.85rem; font-weight: 600; color: var(--c-text, #0f172a); }
.cloud-vis-note { font-size: 0.74rem; color: var(--c-text-dim, #94a3b8); }
.cloud-vis:has(input:checked) { border-color: var(--c-accent, #6366f1); background: var(--c-surface-2, #f1f5f9); }

.cloud-dialog-hint { font-size: 0.74rem; color: var(--c-text-dim, #94a3b8); margin: 0 0 0.6rem; }
/* Hidden until it has something to say -- an empty red line under a form
   reads as an error nobody can name. */
.cloud-dialog-error { font-size: 0.78rem; color: var(--c-danger, #dc2626); margin: 0 0 0.6rem; display: none; }
.cloud-dialog-error.is-shown { display: block; }
.cloud-dialog-actions { display: flex; justify-content: flex-end; gap: 0.5rem; }

.cloud-folder-wrap { position: relative; }
.cloud-folder-wrap .cloud-folder { width: 100%; }
.cloud-folder-gear {
  position: absolute; top: 0.35rem; right: 0.35rem;
  background: none; border: none; cursor: pointer; line-height: 1;
  padding: 0.15rem 0.3rem; border-radius: 0.3rem;
  color: var(--c-text-dim, #94a3b8); font-size: 1rem;
}
.cloud-folder-gear:hover { background: var(--c-surface-2, #f1f5f9); color: var(--c-text, #0f172a); }

/* One file on its own, inside the Cloud viewer.

   ⚠️ max-height IS CAPPED IN VIEWPORT UNITS, not left to the image. A
   500×500 logo and a 6000px photo arrive through the same tag; without
   the cap the second one pushes the name, the size and every control
   below the fold, so the page opens on a wall of pixels with no way to
   tell what you are looking at or get back. contain keeps the aspect
   ratio while the cap does the work. */
.cloud-media-el {
  display: block;
  width: 100%;
  max-height: 70vh;
  object-fit: contain;
  border-radius: 0.5rem;
  background: var(--c-surface-sunken, #f1f5f9);
}
.cloud-media-meta { margin-top: 0.9rem; }
.cloud-media-meta h3 { margin: 0 0 0.2rem; font-size: 1rem; word-break: break-word; }
/* ⚠️ pre-wrap, NOT <br> INJECTION. The description is owner-typed and is
   escaped on the way in; turning its newlines into markup would mean
   emitting tags built from that text, which is the one thing that must
   not happen. CSS keeps the line breaks without any of that. */
.cloud-media-desc {
  margin: 0 0 0.5rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  color: var(--c-text, #0f172a);
  font-size: 0.9rem;
}
/* The edit dialogue's textarea, matching .cloud-dialog-input's box. */
.cloud-dialog-area { resize: vertical; min-height: 5rem; font: inherit; }
.cloud-media-meta p {
  margin: 0 0 0.75rem;
  font-size: 0.82rem;
  color: var(--c-text-dim, #94a3b8);
}
/* The current crumb is the file, and it is not a link. */
.cloud-crumb.is-current {
  color: var(--c-text, #0f172a);
  font-weight: 600;
  padding: 0.15rem 0.3rem;
}
/* ⚠️ The actions wrap rather than overflow: Download / Back / Delete on a
   narrow phone is wider than the screen, and a row that scrolls sideways
   hides Delete behind an edge nobody thinks to drag. */
.cloud-media .cloud-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }

/* The embeddable address. */
.cloud-embed { margin: 0.9rem 0; }
.cloud-embed-label {
  display: block; font-size: 0.75rem; font-weight: 600;
  color: var(--c-text-dim, #94a3b8); margin-bottom: 0.3rem;
}
.cloud-embed-row { display: flex; gap: 0.5rem; align-items: stretch; }
/* ⚠️ min-width:0 OR THE FLEX ITEM REFUSES TO SHRINK. An <input> has an
   intrinsic default width, and in a flex row that becomes a floor -- a
   long S3-shaped URL then pushes Copy off the right edge on a phone. */
.cloud-embed-input {
  flex: 1; min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.75rem; padding: 0.4rem 0.55rem;
  border: 1px solid var(--c-border, #e2e8f0); border-radius: 0.4rem;
  background: var(--c-surface-sunken, #f1f5f9); color: var(--c-text, #0f172a);
}
.cloud-embed-warn {
  margin: 0.4rem 0 0; font-size: 0.75rem;
  color: var(--c-danger, #ef4444);
}

/* Delete, on a folder and on a file.

   ⚠️ IT SITS AWAY FROM THE GEAR, not beside it. The gear opens a dialogue
   somebody can cancel; this one destroys a folder and everything under
   it. Two controls of very different weight, a few pixels apart and both
   grey, is how a misclick happens -- so delete takes the LEFT corner,
   and turns red only on hover so the grid does not read as a row of
   warnings. .cloud-file-wrap exists for the same reason .cloud-folder-wrap
   does: the × must be a SIBLING of the tile button, never nested in it. */
/* ⚠️ THE WRAP IS NOW THE GRID ITEM, not the button. Without the width
   rule the tile collapses to its content and the files grid goes ragged
   -- the same pairing .cloud-folder-wrap already needed above. */
.cloud-file-wrap { position: relative; }
.cloud-file-wrap .cloud-file { width: 100%; }
.cloud-folder-del,
.cloud-file-del {
  position: absolute; top: 0.35rem; left: 0.35rem;
  background: none; border: none; cursor: pointer; line-height: 1;
  padding: 0.15rem 0.35rem; border-radius: 0.3rem;
  color: var(--c-text-faint, #cbd5e1); font-size: 1rem;
  opacity: 0; transition: opacity 0.12s ease;
}
.cloud-folder-wrap:hover .cloud-folder-del,
.cloud-file-wrap:hover .cloud-file-del,
.cloud-folder-del:focus-visible,
.cloud-file-del:focus-visible { opacity: 1; }
.cloud-folder-del:hover,
.cloud-file-del:hover {
  background: var(--c-danger-tint, #fee2e2);
  color: var(--c-danger, #ef4444);
}
/* ⚠️ A hover-revealed control is unreachable on a touch screen, which has
   no hover. On coarse pointers it is simply always visible. */
@media (hover: none) {
  .cloud-folder-del, .cloud-file-del { opacity: 1; }
}
