html, body {
    height: 100%;
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

/* LC-194: readability baseline. A comfortable body line-height + antialiasing
   applied app-wide (Tailwind's preflight leaves line-height to per-element
   utilities; this sets a sane default for prose-y blocks that don't pin one). */
body {
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* LC-194 / LC-218: compact density. Templates opt in with
   `data-lc-density-tight="row|row-dense|header|composer"` on the small
   set of stable container hooks that should respond. Attribute-driven
   (not class-string-coupled) so future utility-class swaps in templates
   do not silently un-compact a surface. The `data-density` attribute
   is set no-flash by the base.html bootstrap from the lc-density
   cookie/localStorage.

   - `row`       halves a tall row's vertical padding. Used by non-follow-up
                  message rows (comfortable `py-2` = 0.5 rem -> compact
                  0.25 rem, the LC-194 chosen ratio).
   - `row-dense` halves a short row's vertical padding. Used by sidebar
                  room/peer rows (comfortable `py-1` = 0.25 rem -> compact
                  0.125 rem so the change is visible at all on rows that
                  were already tight).
   - `header`    shrinks header padding + drops base font ~0.9em. Used by
                  the room topbar.
   - `composer`  halves the composer's TOP padding only (preserves the
                  iOS safe-area bottom inset from LC-199's `lc-pb-safe`).

   Markdown paragraph spacing inside a message also tightens. */
[data-density="compact"] [data-lc-density-tight="row"] {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
}
[data-density="compact"] [data-lc-density-tight="row-dense"] {
    padding-top: 0.125rem;
    padding-bottom: 0.125rem;
}
[data-density="compact"] [data-lc-density-tight="header"] {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
    font-size: 0.9em;
}
[data-density="compact"] [data-lc-density-tight="composer"] {
    padding-top: 0.25rem;
}
[data-density="compact"] .lc-md > p + p,
[data-density="compact"] .lc-md > * + * {
    margin-top: 0.3rem;
}

/* LC-327: message text size (reading scale). Device-local; the <html>
   attribute is set no-flash by the base.html bootstrap from lc-textsize.
   Scoped to .lc-md (the message body) so chrome / timestamps / sidebar are
   unaffected; .lc-md code/pre are em-relative and scale with the base. The
   default ("normal") carries no attribute and inherits the 1rem body size. */
[data-lc-textsize="small"] .lc-md {
    font-size: 0.875rem;
}
[data-lc-textsize="large"] .lc-md {
    font-size: 1.125rem;
}

/* LC-329: copy button on fenced code blocks. The button is icon-only in the
   (locale-independent) cached markdown; layout.html applies its accessible
   name + handles the click. Revealed on hover / keyboard focus so it does not
   clutter the block at rest. */
.lc-codeblock {
    position: relative;
}
/* LC-550: mermaid diagram container. Before the vendored bundle renders (or with
   JS off / on a parse error) the .lc-mermaid-src <pre> shows the raw source as a
   readable fallback; on success mermaid.js replaces the container with an SVG. */
.lc-mermaid {
    margin: 0.5rem 0;
    text-align: center;
}
.lc-mermaid svg {
    max-width: 100%;
    height: auto;
}
.lc-mermaid-src {
    margin: 0;
    padding: 0.75rem;
    overflow-x: auto;
    text-align: left;
    white-space: pre;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8125rem;
    border-radius: 0.375rem;
    border: 1px solid var(--border);
    background: var(--surface-sunken);
    color: var(--content-muted);
}
/* A failed render keeps the source but flags it so it does not read as a
   deliberate code block. */
.lc-mermaid[data-lc-mermaid-error] .lc-mermaid-src {
    border-color: var(--danger);
}
.lc-code-copy {
    position: absolute;
    top: 0.375rem;
    right: 0.375rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    background-color: var(--surface-elevated);
    color: var(--content);
    opacity: 0;
    transition: opacity 0.12s ease-in-out;
    cursor: pointer;
}
.lc-code-copy svg {
    width: 0.95rem;
    height: 0.95rem;
}
.lc-codeblock:hover .lc-code-copy,
.lc-code-copy:focus-visible {
    opacity: 1;
}
.lc-code-copy.lc-code-copied {
    opacity: 1;
}
/* Visible confirmation for sighted users: swap the icon for a checkmark. The
   glyph is locale-independent; the accessible name is updated to the localized
   "Copied" string in layout.html. */
.lc-code-copy.lc-code-copied svg {
    display: none;
}
.lc-code-copy.lc-code-copied::after {
    content: "\2713";
    font-size: 0.95rem;
    line-height: 1;
}

/* LC-308: unread-only sidebar filter. Device-local; the <html> attribute is
   set no-flash by the base.html bootstrap. A row / section is kept only when
   it :has() a non-empty unread or mention badge, or the currently-open room
   (aria-current="page"). Because the badge spans OOB-swap live
   (ws/unread_badge.html), :has()/:empty re-evaluate automatically - a new
   message re-reveals a row and reading it re-hides it, with no JS. The
   read-state badge spans are empty elements, so :not(:empty) is false. */
html[data-lc-unread-only] [data-lc-sb-row]:not(:has([data-lc-unread]:not(:empty), [data-lc-mention]:not(:empty), [aria-current="page"])) {
    display: none;
}
html[data-lc-unread-only] [data-lc-sb-section]:not(:has([data-lc-unread]:not(:empty), [data-lc-mention]:not(:empty), [aria-current="page"])) {
    display: none;
}

/* LC-96 branding hooks. Default values mirror the project's existing
   blue-600 / blue-700 pair so a deployment with no operator-set
   branding looks identical to the pre-LC-96 build. Layout templates
   override these via an inline `<style>:root{...}</style>` block at
   render time so the colors propagate without a Tailwind rebuild. */
:root {
    --brand-primary: #2563eb;
    --brand-accent:  #1d4ed8;
}
.brand-bg          { background-color: var(--brand-primary); }
.brand-bg:hover    { background-color: var(--brand-accent); }
.brand-text        { color: var(--brand-primary); }
.brand-border      { border-color: var(--brand-primary); }

/* LC-190: semantic design tokens (theming foundation for LC-189).
 *
 * These layer ABOVE the LC-96 brand vars and are the single source of truth
 * for surface / text / border / state colors. Tailwind maps utilities onto
 * them (see tailwind.config.js theme.extend.colors), so `bg-surface`,
 * `text-content`, `border-border`, `bg-accent`, etc. recolor automatically
 * when `<html data-theme>` (palette) or `data-mode` (light/dark/hc-light/
 * hc-dark) changes.
 *
 * The `:root` (light) values are the slate/white palette the templates
 * already hardcode, so introducing the tokens is visually a no-op; LC-193
 * migrates templates onto them, and LC-191/LC-192 add the
 * `[data-mode="dark"]` / high-contrast palettes here. */
:root {
    /* LC-554 (Blue Harbor): the neutral ramp is blue-tinted rather than the
       old cool slate. The accent stays #2563eb (the pre-existing brand
       default) so this is a surface/text/border retune, not an accent change.
       Muted/subtle text and borders are a touch darker than the old slate,
       which also nudges contrast up (muted #52657a ~5.6:1 on --surface vs the
       old #64748b ~4.6:1). */
    /* Surfaces */
    --surface:          #f7fbff; /* app background (blue-tinted near-white) */
    --surface-elevated: #ffffff; /* cards, panels, dialogs */
    --surface-sunken:   #edf6ff; /* inputs, code, inset rows */
    /* Text */
    --content:          #071426; /* primary text (near-black navy) */
    --content-muted:    #52657a; /* secondary text */
    --content-subtle:   #8193a8; /* tertiary / placeholder */
    /* Lines */
    --border:           #d8e6f5; /* default borders */
    --border-strong:    #b9cde3; /* emphasized borders */
    /* Accent (chases the operator brand color) */
    --accent:           var(--brand-primary);
    --accent-hover:     var(--brand-accent);
    --accent-content:   #ffffff; /* text/icon on an accent fill */
    /* LC-222: soft-accent callout. Mirrors LC-215's --danger-surface for
       the "tinted accent" surfaces (active sidebar row, mention popover
       avatar, voted poll bar, settings device chip, reply-count chip).
       `--accent-surface-content` is the foreground text color paired with
       it: light themes use `--accent` (preserves blue affordance, ~4.5:1
       on blue-100); dark themes flip to `--content` so text stays
       readable against the dim blue-900 surface (text-accent on dark
       accent-surface fails WCAG AA). */
    --accent-surface:           #dbeafe; /* blue-100 */
    --accent-surface-content:   #1d4ed8;
    /* Semantic state colors (each with a readable foreground) */
    --success:          #16a34a;
    --success-content:  #ffffff;
    --warning:          #d97706;
    --warning-content:  #ffffff;
    --danger:           #dc2626;
    --danger-content:   #ffffff;
    /* LC-215: soft-danger callout panel (delete-account, error banners).
       Surface + border that sit visibly against the page bg as "warning
       zone" without the loud full-fill of `--danger`. Heading + body copy
       on these panels use `--danger` as text color. */
    --danger-surface:   #fef2f2; /* red-50 */
    --danger-border:    #fca5a5; /* red-300 */
    /* LC-224: soft-success + soft-warning callouts (parallel to
       --danger-surface). `*-surface-content` is the foreground text color
       paired with the soft surface; light themes use the saturated text
       color for affordance, dark themes flip to `--content` to keep text
       readable against the dim surface (same posture as LC-222 fixed
       the accent contrast issue). */
    /* Both `*-surface-content` values are hardcoded darker shades (emerald-700,
       amber-700) instead of `var(--success)` / `var(--warning)`. Saturated
       --success (green-600 #16a34a) and --warning (amber-600 #d97706) on
       the soft green-50 / amber-50 surfaces are ~3:1 contrast - fail WCAG
       AA for normal text. The darker emerald-700 / amber-700 shades give
       ~5.5:1 / ~5:1 and pass AA. */
    --success-surface:          #f0fdf4; /* green-50 */
    --success-border:           #86efac; /* green-300 */
    --success-surface-content:  #047857; /* emerald-700 */
    --warning-surface:          #fffbeb; /* amber-50 */
    --warning-border:           #fcd34d; /* amber-300 */
    --warning-surface-content:  #b45309; /* amber-700 */
    /* LC-225: message-actor badges. Three soft-surface families so the
       webhook / email-ingress / protocol-bridge badges dim together in
       dark and hc-dark instead of staying bright. Per-kind hue preserves
       the visual identity ("this is from a webhook" vs "from a bridge");
       all three follow the LC-224 darker-text posture so contrast on the
       soft surface passes WCAG AA. */
    --webhook-surface:          #d1fae5; /* emerald-100 */
    --webhook-content:          #047857; /* emerald-700 */
    --email-surface:            #e0f2fe; /* sky-100 */
    --email-content:            #0369a1; /* sky-700 */
    --bridge-surface:           #ede9fe; /* violet-100 */
    --bridge-content:           #6d28d9; /* violet-700 */
    /* Keyboard-focus ring */
    --ring:             var(--brand-primary);
    /* LC-366: enclave rail. The leftmost app rail reads as a dark Discord/Slack
       chrome surface independent of the page theme; these tokens give it a
       theme-aware identity (idle tile, hover tile, icon color) instead of the
       hardcoded slate-* it used before. Active state reuses --accent. */
    /* LC-560: aligned to the Blue Harbor navy family so the rail + the navy
       sidebar read as one cohesive dark chrome. The rail sits darkest (#041326),
       left of the #071a2f sidebar. */
    --rail-surface:        #041326; /* darkest navy */
    --rail-tile:           #0b2542; /* navy idle tile */
    --rail-tile-hover:     #123e73; /* navy hover tile */
    --rail-content:        #ffffff;
    --rail-content-muted:  #9fb6d0; /* navy-muted */
    /* LC-554 (Blue Harbor) phase 1b: room sidebar (the w-64 nav column, distinct
       from the w-16 enclave rail above). These tokens give the sidebar its own
       dark-navy chrome identity so it reads as a dark shell over the light page
       - the signature Blue Harbor look. In LIGHT mode these mirror the dark
       theme's navy surfaces on purpose. DEFINED here but not yet consumed; the
       sidebar templates still use bg-surface-sunken until LC-560 wires raw
       `.lc-sidebar { background: var(--sidebar-surface) }` rules onto them (same
       pattern as the rail's .lc-rail-tile). Adding the vars is a visual no-op. */
    --sidebar-surface:     #071a2f; /* dark navy chrome over the light page */
    --sidebar-elevated:    #0b2542; /* raised / hover rows */
    --sidebar-sunken:      #041326; /* search field, inset */
    --sidebar-content:     #eaf4ff; /* primary text on navy */
    --sidebar-muted:       #9fb6d0; /* section headers, secondary text */
    --sidebar-border:      #17395c; /* dividers on navy */
}

/* LC-191: dark palette. Same token contract as :root, slate-based dark.
   Applies app-wide once LC-193 migrates templates onto the tokens; until then
   only token-using surfaces (body, and anything already migrated) recolor.
   LC-192 adds the high-contrast variants. */
[data-mode="dark"] {
    /* LC-541 Phase 1: palette-CONSTANT status + actor-badge tokens (keyed by mode only, shared across every palette). Values unchanged. */
    --success:          #22c55e;
    --success-content:  #052e16;
    --warning:          #f59e0b;
    --warning-content:  #451a03;
    --danger:           #ef4444;
    --danger-content:   #450a0a;
    /* LC-215: dim red-tinted panel that recedes against slate-900 surface
       but reads as danger zone. Surface sits just darker than the
       red-950 used by `--danger-content` so the two never compose
       silently invisible if anyone writes `bg-danger-surface
       text-danger-content` by accident. */
    --danger-surface:   #3a0808;
    --danger-border:    #7f1d1d; /* red-900 */
    /* LC-224: dim green / amber tinted panels for soft-success / soft-warning
       on dark surface. Foreground flips to `--content` so text reads on the
       dim surface (saturated success/warning text color would fail AA).
       Surfaces sit just darker than `--success-content` / `--warning-content`
       (which are also green-950 / amber-950 in dark) so the two never
       compose silently invisible if anyone writes `bg-success-surface
       text-success-content` by accident - same posture as LC-215 fixed
       for danger. */
    --success-surface:          #031b0d;
    --success-border:           #14532d; /* green-900 */
    --success-surface-content:  var(--content);
    --warning-surface:          #2d1003;
    --warning-border:           #78350f; /* amber-900 */
    --warning-surface-content:  var(--content);
    /* LC-225: dim emerald / sky / violet tinted badges for the webhook /
       email / bridge actor chips. Content flips to `--content` so text
       reads on the dim per-hue surface. */
    --webhook-surface:          #022c1b;
    --webhook-content:          var(--content);
    --email-surface:            #082f49; /* sky-950 */
    --email-content:            var(--content);
    --bridge-surface:           #2e1065; /* violet-950 */
    --bridge-content:           var(--content);
    color-scheme: dark; /* native form controls + scrollbars go dark */
}
[data-theme="blue-harbor"][data-mode="dark"] {
    /* LC-541 Phase 1: palette-VARYING surface/text/border/accent/ring/rail/sidebar tokens for the Blue Harbor palette. Values unchanged. */
    /* LC-554 (Blue Harbor): navy dark theme, replacing the old slate-900 base.
       Same token contract; surfaces step from #071a2f (page) up to #0b2542
       (elevated) and down to #041326 (sunken). Text ramp is blue-tinted white.
       Accent still tracks --brand-primary; --ring stays blue-400 for contrast
       on the navy ground. */
    --surface:          #071a2f; /* navy page background */
    --surface-elevated: #0b2542; /* cards, panels, dialogs */
    --surface-sunken:   #041326; /* inputs, code, inset rows */
    --content:          #eaf4ff; /* primary text */
    --content-muted:    #9fb6d0; /* secondary text */
    --content-subtle:   #6f89a5; /* tertiary / placeholder */
    --border:           #17395c; /* default borders */
    --border-strong:    #24517c; /* emphasized borders */
    --accent:           var(--brand-primary);
    --accent-hover:     var(--brand-accent);
    --accent-content:   #ffffff;
    /* LC-222: dim brand-tinted panel that sits visibly against the navy
       surface; LC-554 retints it to Blue Harbor's #123e73. `--accent-surface-content`
       flips to `--content` (#eaf4ff) so text reads against the dim blue
       surface; using --accent (blue-600) here would fail WCAG AA. */
    --accent-surface:           #123e73;
    --accent-surface-content:   var(--content);
    --ring:             #60a5fa; /* lighter ring for contrast on dark */
    /* LC-366: rail sits one step darker than the page surface (slate-950 vs
       slate-900) so it still reads as distinct chrome in dark mode. */
    --rail-surface:        #020617; /* slate-950 */
    --rail-tile:           #1e293b; /* slate-800 */
    --rail-tile-hover:     #334155; /* slate-700 */
    --rail-content:        #e2e8f0; /* slate-200 */
    --rail-content-muted:  #94a3b8; /* slate-400 */
    /* LC-554 phase 1b: in dark mode the sidebar sits one step darker than the
       #071a2f page (same posture as the rail above) so it still reads as
       distinct chrome; text ramp matches the page. */
    --sidebar-surface:     #041326; /* one step darker than the navy page */
    --sidebar-elevated:    #16283b; /* raised / hover rows */
    --sidebar-sunken:      #020c18; /* search field, inset */
    --sidebar-content:     #eaf4ff;
    --sidebar-muted:       #9fb6d0;
    --sidebar-border:      #17395c;
}

/* LC-192: high-contrast palettes. Maximal text/surface contrast (AAA for body
   text), pure-black/white borders (always visible, no low-contrast greys), and
   a thicker focus ring (see the `[data-mode="hc-light"]` / `[data-mode="hc-dark"]`
   :focus-visible rules below). State colors use the darker/lighter 700-ish
   shades so they pass AA against the surface. Auto-selected on
   `prefers-contrast: more` when theme = System (resolved in the base.html
   bootstrap).

   LC-554 (Blue Harbor): the hc themes are DELIBERATELY left un-retinted. High
   contrast is a contrast-first, brand-hue-neutral posture (pure black/white
   surfaces + borders); blue-tinting them the way :root and the dark-mode
   block were retuned would lower contrast and defeat their purpose. Their accent is
   blue (blue-800 on hc-light after the LC-541 Task 11 AAA nudge - blue-700 was
   6.70:1, below the 7:1 AAA floor - and blue-300 on hc-dark) and AAA. */
[data-mode="hc-light"] {
    /* LC-541 Phase 1: palette-CONSTANT status + actor-badge tokens (keyed by mode only, shared across every palette). Values unchanged. */
    --success:          #15803d; /* green-700 */
    --success-content:  #ffffff;
    --warning:          #b45309; /* amber-700 */
    --warning-content:  #ffffff;
    --danger:           #b91c1c; /* red-700 */
    --danger-content:   #ffffff;
    /* LC-215: hc-light keeps panel surface bright (red-100) with a strong
       red-800 outline; matches the "everything has a hard outline" hc
       posture. */
    --danger-surface:   #fee2e2; /* red-100 */
    --danger-border:    #991b1b; /* red-800 */
    /* LC-224: hc-light keeps bright success / warning surfaces with
       strong outlines; success-700 / amber-700 text reads on green-100 /
       amber-100 at ~6:1 contrast. */
    --success-surface:          #dcfce7; /* green-100 */
    --success-border:           #15803d; /* green-700 */
    --success-surface-content:  var(--success);
    --warning-surface:          #fef3c7; /* amber-100 */
    --warning-border:           #b45309; /* amber-700 */
    --warning-surface-content:  var(--warning);
    /* LC-225: hc-light keeps bright actor-badge surfaces with darker
       content text for AA contrast. */
    --webhook-surface:          #d1fae5; /* emerald-100 */
    --webhook-content:          #047857; /* emerald-700 */
    --email-surface:            #e0f2fe; /* sky-100 */
    --email-content:            #0369a1; /* sky-700 */
    --bridge-surface:           #ede9fe; /* violet-100 */
    --bridge-content:           #6d28d9; /* violet-700 */
    color-scheme: light;
}
[data-theme="blue-harbor"][data-mode="hc-light"] {
    /* LC-541 Phase 1: palette-VARYING surface/text/border/accent/ring/rail/sidebar tokens for the Blue Harbor palette. Values unchanged. */
    --surface:          #ffffff;
    --surface-elevated: #ffffff;
    --surface-sunken:   #f5f5f5;
    --content:          #000000;
    --content-muted:    #1f2937; /* gray-800: AAA on white, not a faint grey */
    --content-subtle:   #374151; /* gray-700 */
    --border:           #000000;
    --border-strong:    #000000;
    --accent:           #1e40af; /* LC-541 T11: blue-800, AAA on white (blue-700 was 6.70:1) */
    --accent-hover:     #1e3a8a; /* blue-900 */
    --accent-content:   #ffffff;
    /* LC-222: hc-light accent surface keeps the bright blue-100 tint with
       maximal-contrast on top via --accent (blue-700, ~6.5:1 on blue-100). */
    --accent-surface:           #dbeafe; /* blue-100 */
    --accent-surface-content:   var(--accent);
    --ring:             #000000;
    /* LC-366: hc-light rail stays black-chrome with maximal-contrast white
       icons; idle tiles use a dark grey so they separate from the surface
       without relying on a border. */
    --rail-surface:        #000000;
    --rail-tile:           #1f2937; /* gray-800 */
    --rail-tile-hover:     #374151; /* gray-700 */
    --rail-content:        #ffffff;
    --rail-content-muted:  #ffffff;
    /* LC-554 phase 1b: hc-light sidebar is pure-black chrome with white text +
       white dividers, matching the hc rail. Contrast-first, not blue-tinted. */
    --sidebar-surface:     #000000;
    --sidebar-elevated:    #1f2937; /* gray-800 hover row */
    --sidebar-sunken:      #000000;
    --sidebar-content:     #ffffff;
    --sidebar-muted:       #ffffff;
    --sidebar-border:      #ffffff;
}
[data-mode="hc-dark"] {
    /* LC-541 Phase 1: palette-CONSTANT status + actor-badge tokens (keyed by mode only, shared across every palette). Values unchanged. */
    --success:          #4ade80; /* green-400 */
    --success-content:  #000000;
    --warning:          #fbbf24; /* amber-400 */
    --warning-content:  #000000;
    --danger:           #f87171; /* red-400 */
    --danger-content:   #000000;
    /* LC-215: hc-dark keeps the panel surface near-black with a maximal-
       contrast red-200 outline; the border does the visual heavy lifting
       on pure-black ground. Surface uses the same near-black as
       `--surface-elevated` (#0a0a0a) for parity with other hc-dark
       elevated panels; the previous #0a0000 was indistinguishable from
       pure black on most displays. */
    --danger-surface:   #0a0a0a;
    --danger-border:    #fecaca; /* red-200 */
    /* LC-224: hc-dark near-black surface + maximal-contrast green-200 /
       amber-200 borders. --success (green-400) / --warning (amber-400)
       text reads on near-black at ~9:1 contrast. */
    --success-surface:          #0a0a0a;
    --success-border:           #bbf7d0; /* green-200 */
    --success-surface-content:  var(--success);
    --warning-surface:          #0a0a0a;
    --warning-border:           #fde68a; /* amber-200 */
    --warning-surface-content:  var(--warning);
    /* LC-225: hc-dark uses near-black surfaces with bright per-kind text
       colors (emerald-400 / sky-300 / violet-400) for ~9:1 contrast. */
    --webhook-surface:          #0a0a0a;
    --webhook-content:          #34d399; /* emerald-400 */
    --email-surface:            #0a0a0a;
    --email-content:            #7dd3fc; /* sky-300 */
    --bridge-surface:           #0a0a0a;
    --bridge-content:           #c4b5fd; /* violet-300 */
    color-scheme: dark;
}
[data-theme="blue-harbor"][data-mode="hc-dark"] {
    /* LC-541 Phase 1: palette-VARYING surface/text/border/accent/ring/rail/sidebar tokens for the Blue Harbor palette. Values unchanged. */
    --surface:          #000000;
    --surface-elevated: #0a0a0a;
    --surface-sunken:   #000000;
    --content:          #ffffff;
    --content-muted:    #e5e7eb; /* gray-200 */
    --content-subtle:   #d1d5db; /* gray-300 */
    --border:           #ffffff;
    --border-strong:    #ffffff;
    --accent:           #93c5fd; /* blue-300: high contrast on black */
    --accent-hover:     #bfdbfe; /* blue-200 */
    --accent-content:   #000000;
    /* LC-222: hc-dark accent surface uses the same near-black as
       --surface-elevated for parity; --accent (blue-300) on it gives
       ~9.5:1 contrast. */
    --accent-surface:           #0a0a0a;
    --accent-surface-content:   var(--accent);
    --ring:             #ffffff;
    /* LC-366: hc-dark rail is pure black with white icons; idle tiles use a
       dark grey so they read against the black surface. */
    --rail-surface:        #000000;
    --rail-tile:           #1f2937; /* gray-800 */
    --rail-tile-hover:     #374151; /* gray-700 */
    --rail-content:        #ffffff;
    --rail-content-muted:  #ffffff;
    /* LC-554 phase 1b: hc-dark sidebar is pure-black chrome with white text +
       white dividers, matching the hc rail. Contrast-first, not blue-tinted. */
    --sidebar-surface:     #000000;
    --sidebar-elevated:    #1f2937; /* gray-800 hover row */
    --sidebar-sunken:      #000000;
    --sidebar-content:     #ffffff;
    --sidebar-muted:       #ffffff;
    --sidebar-border:      #ffffff;
}

/* LC-541 Phase 1: five non-default palettes. Each defines the palette-VARYING
   surface/text/border/accent/ring/rail/sidebar tokens for its four modes
   (light / dark / hc-light / hc-dark). The palette-CONSTANT status + actor-badge
   tokens come from the [data-mode="..."] blocks above and are NOT redefined here.
   These blocks follow :root (same specificity) so they win when data-theme is set.
   The hc-light / hc-dark surface/text/border/rail/sidebar tokens are inherited
   verbatim from Blue Harbor's contrast-first HC blocks (near-white / near-black,
   already AAA and palette-neutral); only the accent family carries palette
   identity. Accent/accent-content pairs are AAA-verified by
   server/scripts/contrast-check.mjs and nudged where needed (LC-541 Task 11). */

/* Cobalt */
[data-theme="cobalt"] {
  --surface:#f5f9ff; --surface-elevated:#ffffff; --surface-sunken:#eaf3ff;
  --content:#08111f; --content-muted:#53657c; --content-subtle:#7f8ea3;
  --border:#d4e3f7; --border-strong:#aec8eb;
  --accent:#0f62fe; --accent-hover:#0043ce; --accent-content:#ffffff;
  --accent-surface:#d0e2ff; --accent-surface-content:#0043ce; --ring:#4589ff;
  --sidebar-surface:#061932; --sidebar-elevated:#0a2750; --sidebar-sunken:#031022;
  --sidebar-content:#eff6ff; --sidebar-muted:#9db4d1; --sidebar-border:#143d70;
  --rail-surface:#031022; --rail-tile:#0a2750; --rail-tile-hover:#0043ce;
  --rail-content:#ffffff; --rail-content-muted:#9db4d1;
}
[data-theme="cobalt"][data-mode="dark"] {
  --surface:#061932; --surface-elevated:#0a2750; --surface-sunken:#031022;
  --content:#eff6ff; --content-muted:#9db4d1; --content-subtle:#6f86a3;
  --border:#143d70; --border-strong:#1e5a9e;
  --accent:#4589ff; --accent-hover:#78a9ff; --accent-content:#031022;
  --accent-surface:#003a6d; --accent-surface-content:#d0e2ff; --ring:#78a9ff;
  --sidebar-surface:#061932; --sidebar-elevated:#0a2750; --sidebar-sunken:#031022;
  --sidebar-content:#eff6ff; --sidebar-muted:#9db4d1; --sidebar-border:#143d70;
  --rail-surface:#031022; --rail-tile:#0a2750; --rail-tile-hover:#78a9ff;
  --rail-content:#ffffff; --rail-content-muted:#9db4d1;
}
[data-theme="cobalt"][data-mode="hc-light"] {
  --surface:#ffffff; --surface-elevated:#ffffff; --surface-sunken:#f5f5f5;
  --content:#000000; --content-muted:#1f2937; --content-subtle:#374151;
  --border:#000000; --border-strong:#000000;
  --accent:#0043ce; --accent-hover:#002d9c; --accent-content:#ffffff;
  --accent-surface:#d0e2ff; --accent-surface-content:#0043ce; --ring:#000000;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}
[data-theme="cobalt"][data-mode="hc-dark"] {
  --surface:#000000; --surface-elevated:#0a0a0a; --surface-sunken:#000000;
  --content:#ffffff; --content-muted:#e5e7eb; --content-subtle:#d1d5db;
  --border:#ffffff; --border-strong:#ffffff;
  --accent:#78a9ff; --accent-hover:#a6c8ff; --accent-content:#031022;
  --accent-surface:#003a6d; --accent-surface-content:#d0e2ff; --ring:#ffffff;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}

/* Ink + Ice */
[data-theme="ink-ice"] {
  --surface:#f8fbfd; --surface-elevated:#ffffff; --surface-sunken:#eef5fa;
  --content:#0b1623; --content-muted:#586879; --content-subtle:#8491a0;
  --border:#dce7ef; --border-strong:#bfccd8;
  --accent:#1e40af; --accent-hover:#1e3a8a; --accent-content:#ffffff;
  --accent-surface:#dbeafe; --accent-surface-content:#1e3a8a; --ring:#2563eb;
  --sidebar-surface:#07111f; --sidebar-elevated:#142233; --sidebar-sunken:#030914;
  --sidebar-content:#edf6ff; --sidebar-muted:#9aaec4; --sidebar-border:#1b334f;
  --rail-surface:#030914; --rail-tile:#0d1b2d; --rail-tile-hover:#1e3a8a;
  --rail-content:#ffffff; --rail-content-muted:#9aaec4;
}
[data-theme="ink-ice"][data-mode="dark"] {
  --surface:#07111f; --surface-elevated:#0d1b2d; --surface-sunken:#030914;
  --content:#edf6ff; --content-muted:#9aaec4; --content-subtle:#687f98;
  --border:#1b334f; --border-strong:#2b4d72;
  --accent:#60a5fa; --accent-hover:#93c5fd; --accent-content:#06101f;
  --accent-surface:#172554; --accent-surface-content:#dbeafe; --ring:#60a5fa;
  --sidebar-surface:#07111f; --sidebar-elevated:#142233; --sidebar-sunken:#030914;
  --sidebar-content:#edf6ff; --sidebar-muted:#9aaec4; --sidebar-border:#1b334f;
  --rail-surface:#030914; --rail-tile:#0d1b2d; --rail-tile-hover:#93c5fd;
  --rail-content:#ffffff; --rail-content-muted:#9aaec4;
}
[data-theme="ink-ice"][data-mode="hc-light"] {
  --surface:#ffffff; --surface-elevated:#ffffff; --surface-sunken:#f5f5f5;
  --content:#000000; --content-muted:#1f2937; --content-subtle:#374151;
  --border:#000000; --border-strong:#000000;
  --accent:#1e40af; --accent-hover:#1e3a8a; --accent-content:#ffffff;
  --accent-surface:#dbeafe; --accent-surface-content:#1e3a8a; --ring:#000000;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}
[data-theme="ink-ice"][data-mode="hc-dark"] {
  --surface:#000000; --surface-elevated:#0a0a0a; --surface-sunken:#000000;
  --content:#ffffff; --content-muted:#e5e7eb; --content-subtle:#d1d5db;
  --border:#ffffff; --border-strong:#ffffff;
  --accent:#60a5fa; --accent-hover:#93c5fd; --accent-content:#06101f;
  --accent-surface:#172554; --accent-surface-content:#dbeafe; --ring:#ffffff;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}

/* Arctic Messenger */
[data-theme="arctic"] {
  --surface:#fbfdff; --surface-elevated:#ffffff; --surface-sunken:#f0f7ff;
  --content:#0f172a; --content-muted:#5f7288; --content-subtle:#8493a4;
  --border:#d8e4ee; --border-strong:#c4d7eb;
  --accent:#0369a1; --accent-hover:#075985; --accent-content:#ffffff;
  --accent-surface:#e0f2fe; --accent-surface-content:#0369a1; --ring:#0ea5e9;
  --sidebar-surface:#062033; --sidebar-elevated:#0a2c47; --sidebar-sunken:#031522;
  --sidebar-content:#ecfeff; --sidebar-muted:#9bc4d6; --sidebar-border:#174764;
  --rail-surface:#031522; --rail-tile:#0a2c47; --rail-tile-hover:#0369a1;
  --rail-content:#ffffff; --rail-content-muted:#9bc4d6;
}
[data-theme="arctic"][data-mode="dark"] {
  --surface:#062033; --surface-elevated:#0a2c47; --surface-sunken:#031522;
  --content:#ecfeff; --content-muted:#9bc4d6; --content-subtle:#6f9aad;
  --border:#174764; --border-strong:#246487;
  --accent:#38bdf8; --accent-hover:#7dd3fc; --accent-content:#031522;
  --accent-surface:#075985; --accent-surface-content:#e0f2fe; --ring:#38bdf8;
  --sidebar-surface:#062033; --sidebar-elevated:#0a2c47; --sidebar-sunken:#031522;
  --sidebar-content:#ecfeff; --sidebar-muted:#9bc4d6; --sidebar-border:#174764;
  --rail-surface:#031522; --rail-tile:#0a2c47; --rail-tile-hover:#7dd3fc;
  --rail-content:#ffffff; --rail-content-muted:#9bc4d6;
}
[data-theme="arctic"][data-mode="hc-light"] {
  --surface:#ffffff; --surface-elevated:#ffffff; --surface-sunken:#f5f5f5;
  --content:#000000; --content-muted:#1f2937; --content-subtle:#374151;
  --border:#000000; --border-strong:#000000;
  --accent:#075985; --accent-hover:#0c4a6e; --accent-content:#ffffff;
  --accent-surface:#e0f2fe; --accent-surface-content:#0369a1; --ring:#000000;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}
[data-theme="arctic"][data-mode="hc-dark"] {
  --surface:#000000; --surface-elevated:#0a0a0a; --surface-sunken:#000000;
  --content:#ffffff; --content-muted:#e5e7eb; --content-subtle:#d1d5db;
  --border:#ffffff; --border-strong:#ffffff;
  --accent:#38bdf8; --accent-hover:#7dd3fc; --accent-content:#031522;
  --accent-surface:#075985; --accent-surface-content:#e0f2fe; --ring:#ffffff;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}

/* Deep Sea Cyan */
[data-theme="deep-sea"] {
  --surface:#f5fcff; --surface-elevated:#ffffff; --surface-sunken:#e8f7fb;
  --content:#071b24; --content-muted:#56707a; --content-subtle:#7f949c;
  --border:#d3eaf0; --border-strong:#afd1dc;
  --accent:#0e7490; --accent-hover:#155e75; --accent-content:#ffffff;
  --accent-surface:#cffafe; --accent-surface-content:#0e7490; --ring:#06b6d4;
  --sidebar-surface:#041923; --sidebar-elevated:#082836; --sidebar-sunken:#020f15;
  --sidebar-content:#ecfeff; --sidebar-muted:#9bc6d0; --sidebar-border:#164150;
  --rail-surface:#020f15; --rail-tile:#082836; --rail-tile-hover:#0e7490;
  --rail-content:#ffffff; --rail-content-muted:#9bc6d0;
}
[data-theme="deep-sea"][data-mode="dark"] {
  --surface:#041923; --surface-elevated:#082836; --surface-sunken:#020f15;
  --content:#ecfeff; --content-muted:#9bc6d0; --content-subtle:#6f98a3;
  --border:#164150; --border-strong:#236173;
  --accent:#22d3ee; --accent-hover:#67e8f9; --accent-content:#021014;
  --accent-surface:#164e63; --accent-surface-content:#cffafe; --ring:#22d3ee;
  --sidebar-surface:#041923; --sidebar-elevated:#082836; --sidebar-sunken:#020f15;
  --sidebar-content:#ecfeff; --sidebar-muted:#9bc6d0; --sidebar-border:#164150;
  --rail-surface:#020f15; --rail-tile:#082836; --rail-tile-hover:#67e8f9;
  --rail-content:#ffffff; --rail-content-muted:#9bc6d0;
}
[data-theme="deep-sea"][data-mode="hc-light"] {
  --surface:#ffffff; --surface-elevated:#ffffff; --surface-sunken:#f5f5f5;
  --content:#000000; --content-muted:#1f2937; --content-subtle:#374151;
  --border:#000000; --border-strong:#000000;
  --accent:#155e75; --accent-hover:#164e63; --accent-content:#ffffff;
  --accent-surface:#cffafe; --accent-surface-content:#0e7490; --ring:#000000;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}
[data-theme="deep-sea"][data-mode="hc-dark"] {
  --surface:#000000; --surface-elevated:#0a0a0a; --surface-sunken:#000000;
  --content:#ffffff; --content-muted:#e5e7eb; --content-subtle:#d1d5db;
  --border:#ffffff; --border-strong:#ffffff;
  --accent:#22d3ee; --accent-hover:#67e8f9; --accent-content:#021014;
  --accent-surface:#164e63; --accent-surface-content:#cffafe; --ring:#ffffff;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}

/* Royal Navy */
[data-theme="royal-navy"] {
  --surface:#f6f8ff; --surface-elevated:#ffffff; --surface-sunken:#edf2ff;
  --content:#0b1026; --content-muted:#5c6680; --content-subtle:#858ea6;
  --border:#dae2f5; --border-strong:#bbc8e5;
  --accent:#1d4ed8; --accent-hover:#1e3a8a; --accent-content:#ffffff;
  --accent-surface:#dbeafe; --accent-surface-content:#1e3a8a; --ring:#2563eb;
  --sidebar-surface:#080f2a; --sidebar-elevated:#101b45; --sidebar-sunken:#050a1a;
  --sidebar-content:#eff3ff; --sidebar-muted:#a3aed0; --sidebar-border:#23306a;
  --rail-surface:#050a1a; --rail-tile:#101b45; --rail-tile-hover:#1e3a8a;
  --rail-content:#ffffff; --rail-content-muted:#a3aed0;
}
[data-theme="royal-navy"][data-mode="dark"] {
  --surface:#080f2a; --surface-elevated:#101b45; --surface-sunken:#050a1a;
  --content:#eff3ff; --content-muted:#a3aed0; --content-subtle:#737fa5;
  --border:#23306a; --border-strong:#35479a;
  --accent:#60a5fa; --accent-hover:#93c5fd; --accent-content:#050a1a;
  --accent-surface:#1e3a8a; --accent-surface-content:#dbeafe; --ring:#60a5fa;
  --sidebar-surface:#080f2a; --sidebar-elevated:#101b45; --sidebar-sunken:#050a1a;
  --sidebar-content:#eff3ff; --sidebar-muted:#a3aed0; --sidebar-border:#23306a;
  --rail-surface:#050a1a; --rail-tile:#101b45; --rail-tile-hover:#93c5fd;
  --rail-content:#ffffff; --rail-content-muted:#a3aed0;
}
[data-theme="royal-navy"][data-mode="hc-light"] {
  --surface:#ffffff; --surface-elevated:#ffffff; --surface-sunken:#f5f5f5;
  --content:#000000; --content-muted:#1f2937; --content-subtle:#374151;
  --border:#000000; --border-strong:#000000;
  --accent:#1e40af; --accent-hover:#1e3a8a; --accent-content:#ffffff;
  --accent-surface:#dbeafe; --accent-surface-content:#1e3a8a; --ring:#000000;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}
[data-theme="royal-navy"][data-mode="hc-dark"] {
  --surface:#000000; --surface-elevated:#0a0a0a; --surface-sunken:#000000;
  --content:#ffffff; --content-muted:#e5e7eb; --content-subtle:#d1d5db;
  --border:#ffffff; --border-strong:#ffffff;
  --accent:#60a5fa; --accent-hover:#93c5fd; --accent-content:#050a1a;
  --accent-surface:#1e3a8a; --accent-surface-content:#dbeafe; --ring:#ffffff;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}

/* Amethyst - the violet palette (LC mockup pixel-match). Same token contract as
   cobalt; hc-light/hc-dark inherit blue-harbor's contrast-first neutral surfaces
   and only carry violet identity in the accent family. */
[data-theme="amethyst"] {
  --surface:#faf8ff; --surface-elevated:#ffffff; --surface-sunken:#f3eeff;
  --content:#160f24; --content-muted:#5b5270; --content-subtle:#8a80a3;
  --border:#e6ddf7; --border-strong:#cebfec;
  --accent:#7c3aed; --accent-hover:#6d28d9; --accent-content:#ffffff;
  --accent-surface:#ede4ff; --accent-surface-content:#6d28d9; --ring:#8b5cf6;
  --sidebar-surface:#1a1030; --sidebar-elevated:#2b1d46; --sidebar-sunken:#120a22;
  --sidebar-content:#f3eeff; --sidebar-muted:#b6a9d1; --sidebar-border:#33245a;
  --rail-surface:#120a22; --rail-tile:#241640; --rail-tile-hover:#6d28d9;
  --rail-content:#ffffff; --rail-content-muted:#b6a9d1;
}
[data-theme="amethyst"][data-mode="dark"] {
  --surface:#1a1030; --surface-elevated:#241640; --surface-sunken:#120a22;
  --content:#f3eeff; --content-muted:#b6a9d1; --content-subtle:#8776a3;
  --border:#33245a; --border-strong:#4a3680;
  --accent:#a78bfa; --accent-hover:#c4b5fd; --accent-content:#120a22;
  --accent-surface:#3a2a6b; --accent-surface-content:#ede4ff; --ring:#c4b5fd;
  --sidebar-surface:#120a22; --sidebar-elevated:#2b1d46; --sidebar-sunken:#0d0719;
  --sidebar-content:#f3eeff; --sidebar-muted:#b6a9d1; --sidebar-border:#33245a;
  --rail-surface:#0d0719; --rail-tile:#241640; --rail-tile-hover:#c4b5fd;
  --rail-content:#ffffff; --rail-content-muted:#b6a9d1;
}
[data-theme="amethyst"][data-mode="hc-light"] {
  --surface:#ffffff; --surface-elevated:#ffffff; --surface-sunken:#f5f5f5;
  --content:#000000; --content-muted:#1f2937; --content-subtle:#374151;
  --border:#000000; --border-strong:#000000;
  --accent:#6b21a8; --accent-hover:#581c87; --accent-content:#ffffff;
  --accent-surface:#ede4ff; --accent-surface-content:#6b21a8; --ring:#000000;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}
[data-theme="amethyst"][data-mode="hc-dark"] {
  --surface:#000000; --surface-elevated:#0a0a0a; --surface-sunken:#000000;
  --content:#ffffff; --content-muted:#e5e7eb; --content-subtle:#d1d5db;
  --border:#ffffff; --border-strong:#ffffff;
  --accent:#d8b4fe; --accent-hover:#e9d5ff; --accent-content:#000000;
  --accent-surface:#2e1065; --accent-surface-content:#d8b4fe; --ring:#ffffff;
  --sidebar-surface:#000000; --sidebar-elevated:#1f2937; --sidebar-sunken:#000000;
  --sidebar-content:#ffffff; --sidebar-muted:#ffffff; --sidebar-border:#ffffff;
  --rail-surface:#000000; --rail-tile:#1f2937; --rail-tile-hover:#374151;
  --rail-content:#ffffff; --rail-content-muted:#ffffff;
}

/* Global keyboard-focus indicator. Applies on Tab / arrow / programmatic
   focus but NOT on plain mouse clicks (`:focus-visible` heuristic). Pinned
   to blue-600 so it matches the per-component `focus:ring-blue-500` already
   used in `notify_dropdown.html`, `sidebar.html`, and `own_avatar_oob.html`.
   Those three elements keep their custom ring: their `focus:outline-none`
   utility has higher specificity than this selector and wins. */
:focus-visible {
    outline: 2px solid var(--ring, #2563eb);
    outline-offset: 2px;
    border-radius: 2px;
}
/* LC-192: thicker, offset focus ring in high-contrast themes. */
[data-mode="hc-light"] :focus-visible,
[data-mode="hc-dark"] :focus-visible {
    outline-width: 3px;
    outline-offset: 3px;
}

#main {
    /* LC-199: with the LC-199 viewport-fit=cover meta in base.html, plain
     * 100vh on iOS Safari includes the URL bar / dynamic-toolbar area, so
     * content extends behind the chrome and scrolls under it. `100dvh`
     * (dynamic viewport height) excludes the URL bar - it shrinks/grows as
     * the toolbar shows/hides. Falls back to `100vh` on browsers that don't
     * yet understand `dvh` (Safari < 15.4, Firefox < 110). */
    height: 100vh;
    height: 100dvh;
}

/* LC-569: UI scaling. The whole app is rem-based (Tailwind utilities + the .lc-*
   layer), so scaling the root font-size scales type AND spacing together, which
   is what the "Scaling" appearance setting does. Stamped on <html> before paint
   by the base.html bootstrap (data-scale), so there is no flash. */
html[data-scale="compact"] { font-size: 14.5px; }
html[data-scale="default"] { font-size: 16px; }
html[data-scale="large"]   { font-size: 18px; }
html[data-scale="xl"]      { font-size: 20px; }

/* Thin, button-less, thumb-only scrollbars that appear on hover of the scroll
   area. Neutral translucent grey so they read on any palette + mode. */
* { scrollbar-width: thin; scrollbar-color: transparent transparent; }
*:hover { scrollbar-color: rgba(128,128,128,.42) transparent; }

::-webkit-scrollbar {
    width: 11px;
    height: 11px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-button { display: none; width: 0; height: 0; } /* no up/down triangles */
::-webkit-scrollbar-corner { background: transparent; }

::-webkit-scrollbar-thumb {
    background: transparent;
    border: 3px solid transparent;
    border-radius: 999px;
    background-clip: content-box;
}

:hover::-webkit-scrollbar-thumb { background: rgba(128,128,128,.42); background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: rgba(128,128,128,.68); background-clip: content-box; }

/* Message body rendered as markdown.
 *
 * `whitespace-pre-wrap` is intentionally not applied at the container level
 * because pulldown_cmark already emits explicit `<br>` for newlines (we map
 * SoftBreak -> HardBreak server-side). The rules below restore minimal
 * typography for elements Tailwind's preflight strips. Single-paragraph
 * messages (the common case) render with no extra vertical margin so the
 * row height matches what plain-text rendering used to produce. */
.lc-md > p {
    margin: 0;
}
.lc-md > p + p,
.lc-md > * + * {
    margin-top: 0.5rem;
}
.lc-md strong { font-weight: 600; }
.lc-md em { font-style: italic; }
/* LC-221: tokenized so strike + link recolor with the active theme and
   follow the operator's branded accent. Pre-LC-221 used hardcoded
   `#64748b` (slate-500) and `#2563eb` (blue-600) which stayed constant
   across all four themes. */
.lc-md del { text-decoration: line-through; color: var(--content-muted); }
/* LC-290: spoiler. `||text||` renders as a filled box; the inner text is
   transparent until revealed (one-way, on click / Enter / Space via the
   layout.html delegated handler). Box recolors with the active theme. */
.lc-spoiler {
    background: var(--content-muted);
    border-radius: 0.25rem;
    padding: 0 0.2rem;
    cursor: pointer;
    transition: background 0.12s ease;
}
.lc-spoiler-inner { opacity: 0; }
.lc-spoiler-revealed { background: transparent; cursor: text; }
.lc-spoiler-revealed .lc-spoiler-inner { opacity: 1; }
.lc-md a { color: var(--accent); }
.lc-md a:hover { color: var(--accent-hover); text-decoration: underline; }
.lc-md ul, .lc-md ol { padding-left: 1.5rem; margin: 0.25rem 0; }
.lc-md ul { list-style: disc; }
.lc-md ol { list-style: decimal; }
.lc-md li { margin: 0.125rem 0; }
.lc-md blockquote {
    border-left: 3px solid var(--border-strong);
    padding: 0.125rem 0.75rem;
    margin: 0.25rem 0;
    color: var(--content-muted);
}
.lc-md h1, .lc-md h2, .lc-md h3, .lc-md h4, .lc-md h5, .lc-md h6 {
    font-weight: 700;
    margin: 0.5rem 0 0.25rem;
    line-height: 1.25;
}
.lc-md h1 { font-size: 1.25rem; }
.lc-md h2 { font-size: 1.15rem; }
.lc-md h3 { font-size: 1.05rem; }
.lc-md h4, .lc-md h5, .lc-md h6 { font-size: 1rem; }
.lc-md table { border-collapse: collapse; margin: 0.25rem 0; }
.lc-md th, .lc-md td {
    border: 1px solid var(--border);
    padding: 0.25rem 0.5rem;
}
.lc-md th { background: var(--surface-sunken); font-weight: 600; }
.lc-md hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 0.5rem 0;
}
.lc-md-code,
.lc-md code:not(pre code) {
    background: var(--surface-sunken);
    color: var(--content);
    padding: 0.05rem 0.3rem;
    border-radius: 0.25rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
    font-size: 0.9em;
}
.lc-md pre {
    background: var(--surface-elevated);
  color: var(--content);
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    padding: 0.5rem 0.75rem;
    margin: 0.25rem 0;
    overflow-x: auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
    font-size: 0.85em;
    line-height: 1.4;
    /* syntect emits inline `background-color` on the <pre>; override so the
       app's neutral surface wins. The token colours inside still apply. */
    background-color: var(--surface-elevated) !important;
}
.lc-md pre code { background: transparent; padding: 0; }

/* LC-284: self-mention highlight. A message that @-mentions the viewer gets a
   layout-neutral left accent bar (inset box-shadow, so the row box is
   unchanged) plus a faint accent wash. Theme-aware via --accent; hover still
   shows the sunken background (the Tailwind :hover utility wins). */
.lc-mentioned {
    box-shadow: inset 3px 0 0 0 var(--accent);
    /* LC-558: wash tracks the palette's --accent-surface token (was a hard-coded
       accent 8% mix that ignored the token and read too faint). ~55% keeps it
       obvious without going radioactive; the 3px bar stays the primary signal,
       and hover still wins with the sunken background. */
    background: color-mix(in srgb, var(--accent-surface) 55%, transparent);
}
/* LC-553: consecutive mentioned messages (a run of pings, e.g. follow-ups from
   the same author) read as separate notched brackets because each keeps its own
   rounded corners. Square the corners where two mentioned rows touch so the
   accent bar + wash merge into one continuous block for the whole run. */
.lc-mentioned:has(+ .lc-mentioned) {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}
.lc-mentioned + .lc-mentioned {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

/* LC-272: copy button on code blocks. The enhancer (layout.html) wraps each
   <pre> in .lc-code-wrap and drops a .lc-code-copy button in the top-right,
   revealed on hover/focus. */
.lc-code-wrap {
    position: relative;
}
.lc-code-copy {
    position: absolute;
    top: 0.35rem;
    right: 0.35rem;
    padding: 0.1rem 0.4rem;
    font-size: 0.7rem;
    line-height: 1.2;
    border: 1px solid var(--border);
    border-radius: 0.25rem;
    background: var(--surface-sunken);
    color: var(--content-muted);
    opacity: 0;
    transition: opacity 0.15s ease;
    cursor: pointer;
}
.lc-code-wrap:hover .lc-code-copy,
.lc-code-copy:focus {
    opacity: 1;
}
.lc-code-copy:hover {
    color: var(--content);
}

/* Quote-reply UI.
 *
 * The chip is rendered inline above a message body to show the message it
 * is responding to. A separate "reply bar" variant lives inside the
 * composer; both share the same color palette but the in-message chip is
 * compact and clickable while the composer bar adds an "x" to cancel.
 */
/* LC-461: inline quote-reply reference. A QUIET, muted context line - thin
   neutral left-border, no fill, small low-contrast text - so it stays
   subordinate to the message body below it. Deliberately NOT accent-tinted:
   the accent treatment is reserved for the thread-entry pill (the interactive
   door to the side panel), keeping the two reply concepts visually distinct. */
.lc-quote-chip {
    display: flex;
    align-items: center;
    gap: 0.3125rem;
    margin-bottom: 0.1875rem;
    padding: 0.0625rem 0.5rem;
    border-left: 2px solid var(--border-strong);
    background: transparent;
    border-radius: 0;
    font-size: 0.8em;
    color: var(--content-subtle);
    text-decoration: none;
    max-width: 100%;
    width: fit-content;
}
.lc-quote-chip:hover {
    background: var(--surface-sunken);
    color: var(--content-muted);
}
.lc-quote-ico {
    height: 0.8em;
    width: 0.8em;
    flex: none;
    color: var(--content-subtle);
}
.lc-quote-label {
    color: var(--content-subtle);
    white-space: nowrap;
}
.lc-quote-author {
    font-weight: 600;
    color: var(--content-muted);
    white-space: nowrap;
}
.lc-quote-excerpt {
    color: var(--content-subtle);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.lc-quote-deleted {
    font-style: italic;
    color: var(--content-subtle);
}

/* LC-460: thread-entry chip under a message (replaces the speech-balloon emoji
   pill). Branch icon + count + chevron read as "open the thread", visually
   distinct from the quoted-reply pointer above. */
.lc-thread-entry {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    margin-top: 0.25rem;
    padding: 0.1875rem 0.5rem 0.1875rem 0.4375rem;
    border: 1px solid var(--accent-border, var(--border));
    border-radius: 9999px;
    background: var(--accent-surface);
    color: var(--accent-surface-content);
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    transition: background-color 0.12s ease, border-color 0.12s ease;
}
.lc-thread-entry:hover { background: var(--surface-sunken); border-color: var(--accent); }
.lc-thread-entry:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); }
.lc-thread-entry-ico { height: 0.9rem; width: 0.9rem; flex: none; color: var(--accent); }
.lc-thread-entry-count { white-space: nowrap; }
.lc-thread-entry-chevron { height: 0.8rem; width: 0.8rem; flex: none; color: var(--content-subtle); margin-left: -0.125rem; }

/* LC-460: thread panel hierarchy. A count divider labels the replies section and
   a left rail nests replies beneath the root message. */
.lc-thread-count {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.75rem 0.125rem;
}
.lc-thread-count-rule { height: 1px; flex: 1; background: var(--border); }
.lc-thread-count-label {
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--content-subtle);
    white-space: nowrap;
}
/* LC-462: the replies list. The earlier heavy accent rail down the left edge
   read as a "wall"; the parent block + count divider already convey hierarchy,
   so the rail is gone and the panel just scrolls.
   LC-576: this used to also pin its own always-visible 8px --border-strong
   scrollbar here, which outlived the global switch to thin, button-less,
   hover-only scrollbars and left the thread panel as the one surface still
   showing a resting bar. It now inherits the global rule; no override needed. */

.lc-quote-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
    padding: 0.25rem 0.5rem;
    border-left: 3px solid var(--accent);
    background: var(--accent-surface);
    border-radius: 0.25rem;
    font-size: 0.8em;
    color: var(--accent-surface-content);
}
.lc-quote-bar-inner {
    display: flex;
    gap: 0.4rem;
    overflow: hidden;
    align-items: baseline;
    min-width: 0;
    flex: 1;
}
.lc-quote-bar-label {
    color: var(--content-muted);
}
.lc-quote-bar-author {
    font-weight: 600;
    white-space: nowrap;
}
.lc-quote-bar-excerpt {
    color: var(--content-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.lc-quote-bar-clear {
    color: var(--content-muted);
    padding: 0 0.25rem;
    line-height: 1;
}
.lc-quote-bar-clear:hover {
    color: var(--content);
}

/* Voice-message player (partials/attachment.html). Wired by the voice IIFE
   in layout.html. */
.lc-voice {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    background: #fff;
}
.lc-voice-play {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 9999px;
    background: #2563eb;
    color: #fff;
    flex: none;
}
.lc-voice-play:hover {
    background: #1d4ed8;
}
/* Brief buffering state between the play click and canplaythrough. */
.lc-voice[data-loading] .lc-voice-play {
    opacity: 0.5;
    cursor: progress;
}
.lc-voice-play svg {
    width: 1.1rem;
    height: 1.1rem;
}
/* Play / pause icon swap is driven by the [data-playing] attribute the IIFE
   toggles on the container. */
.lc-voice-icon-pause {
    display: none;
}
.lc-voice[data-playing] .lc-voice-icon-play {
    display: none;
}
.lc-voice[data-playing] .lc-voice-icon-pause {
    display: inline;
}
.lc-voice-wave {
    width: 320px;
    max-width: 50vw;
    height: 40px;
    cursor: pointer;
}
.lc-voice-time {
    font-size: 0.75rem;
    color: #64748b;
    font-variant-numeric: tabular-nums;
    flex: none;
}

/* LC-499: voice-message composer states (recording card, preview, errors). */
.lc-composer-btn.is-recording {
    background-color: color-mix(in srgb, var(--danger) 14%, transparent);
    color: var(--danger);
    box-shadow: inset 0 0 0 1.5px var(--danger);
}
.lc-composer-btn.is-recording:hover {
    background-color: color-mix(in srgb, var(--danger) 22%, transparent);
    color: var(--danger);
}

/* Recording-in-progress card, anchored just above the composer input. */
.lc-rec {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--border));
    border-radius: 0.625rem;
    background: color-mix(in srgb, var(--danger) 7%, var(--surface-elevated));
}
.lc-rec-dot {
    flex: none;
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 9999px;
    background: var(--danger);
    animation: lc-rec-pulse 1.4s ease-in-out infinite;
}
.lc-rec-label {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--danger);
    flex: none;
}
.lc-rec-time {
    font-size: 0.875rem;
    font-variant-numeric: tabular-nums;
    color: var(--content);
    flex: none;
}
/* Live mic-level meter: each bar is scaled vertically by the analyser. */
.lc-rec-level {
    display: flex;
    align-items: center;
    gap: 2px;
    height: 1.5rem;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}
.lc-rec-level i {
    display: block;
    flex: 1 1 0;
    min-width: 2px;
    max-width: 4px;
    height: 100%;
    border-radius: 9999px;
    background: color-mix(in srgb, var(--danger) 55%, var(--content-subtle));
    transform: scaleY(0.12);
    transform-origin: center;
    transition: transform 90ms linear;
}
.lc-rec-actions {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    flex: none;
}
.lc-rec-stop {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.7rem;
    border-radius: 0.5rem;
    font-size: 0.8125rem;
    font-weight: 600;
    background: var(--danger);
    color: #fff;
}
.lc-rec-stop:hover { background: color-mix(in srgb, var(--danger) 85%, #000); }
.lc-rec-cancel {
    padding: 0.3rem 0.6rem;
    border-radius: 0.5rem;
    font-size: 0.8125rem;
    color: var(--content-muted);
}
.lc-rec-cancel:hover { background: var(--surface-sunken); color: var(--content); }

/* Upload-in-progress variant: neutral card + spinner. */
.lc-rec--busy {
    border-color: var(--border);
    background: var(--surface-elevated);
  color: var(--content);
}
.lc-rec--busy .lc-rec-label { color: var(--content-muted); }
.lc-rec-spinner {
    flex: none;
    width: 0.9rem;
    height: 0.9rem;
    border-radius: 9999px;
    border: 2px solid var(--border);
    border-top-color: var(--accent);
    animation: lc-rec-spin 0.7s linear infinite;
}

/* Recorded-and-attached preview: the inline player + re-record / remove. */
.lc-recpreview {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    padding: 0.5rem 0.625rem;
    border: 1px solid var(--border);
    border-radius: 0.625rem;
    background: var(--surface-elevated);
  color: var(--content);
}
.lc-recpreview-actions {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    flex: none;
}
.lc-recpreview-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.6rem;
    border-radius: 0.5rem;
    font-size: 0.8125rem;
    color: var(--content-muted);
}
.lc-recpreview-btn svg { width: 0.95rem; height: 0.95rem; }
.lc-recpreview-btn:hover { background: var(--surface-sunken); color: var(--content); }
.lc-recpreview-btn--danger:hover {
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    color: var(--danger);
}

/* Voice error card (mic denied / unsupported / upload failed + retry). */
.lc-rec-error {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--border));
    border-radius: 0.625rem;
    background: color-mix(in srgb, var(--danger) 8%, var(--surface-elevated));
    color: var(--danger);
}
.lc-rec-error-icon { flex: none; width: 1.1rem; height: 1.1rem; margin-top: 0.05rem; }
.lc-rec-error-body { display: flex; flex-direction: column; gap: 0.1rem; flex: 1 1 auto; min-width: 0; }
.lc-rec-error-help { color: var(--content-muted); font-size: 0.75rem; }
.lc-rec-error-retry {
    flex: none;
    align-self: center;
    padding: 0.25rem 0.6rem;
    border-radius: 0.5rem;
    font-size: 0.8125rem;
    font-weight: 600;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--danger) 45%, transparent);
    color: var(--danger);
}
.lc-rec-error-retry:hover { background: color-mix(in srgb, var(--danger) 12%, transparent); }

@keyframes lc-rec-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.35; transform: scale(0.8); }
}
@keyframes lc-rec-spin {
    to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
    .lc-rec-dot { animation: none; }
    .lc-rec-spinner { animation-duration: 1.4s; }
    .lc-rec-level i { transition: none; }
}

/* LC-480: announcement-channel banner (above the timeline) + the read-only
   composer notice shown to members who cannot post. */
.lc-announce-strip {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 1rem;
    font-size: 0.8125rem;
    color: var(--content-muted);
    background: color-mix(in srgb, var(--accent) 8%, var(--surface-elevated));
    border-bottom: 1px solid var(--border);
}
.lc-announce-strip svg { width: 1rem; height: 1rem; flex: none; color: var(--accent); }
.lc-announce-strip-label {
    font-weight: 600;
    color: var(--accent);
    text-transform: uppercase;
    font-size: 0.6875rem;
    letter-spacing: 0.04em;
    flex: none;
}
.lc-readonly-notice {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    border-top: 1px solid var(--border);
    padding: 0.75rem 1rem;
    font-size: 0.875rem;
    color: var(--content-muted);
}
.lc-readonly-notice svg { width: 1rem; height: 1rem; flex: none; color: var(--content-subtle); }

/* LC-500: composer file-attachment chip (matches the .lc-recpreview voice
   card). Image thumbnail or file-type icon + truncated name + size + Remove. */
.lc-attach {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5rem 0.625rem;
    border: 1px solid var(--border);
    border-radius: 0.625rem;
    background: var(--surface-elevated);
  color: var(--content);
}
.lc-attach-thumb {
    flex: none;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 0.5rem;
    object-fit: cover;
    background: var(--surface-sunken);
}
.lc-attach-icon {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 0.5rem;
    background: color-mix(in srgb, var(--accent) 10%, var(--surface-sunken));
    color: var(--accent);
}
.lc-attach-icon svg { width: 1.4rem; height: 1.4rem; }
/* Spinner overlay while the upload is in flight. */
.lc-attach-thumbwrap { position: relative; flex: none; display: inline-flex; }
.lc-attach-spin {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 0.5rem;
    background: color-mix(in srgb, var(--surface-elevated) 55%, transparent);
}
.lc-attach-meta {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1 1 auto;
}
.lc-attach-name {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--content);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.lc-attach-size {
    font-size: 0.75rem;
    color: var(--content-muted);
}

/* LC-500: timeline download card for non-image attachments (file_card-style
   icon + truncated name + size), upgrading the old plain bordered link. */
.lc-filecard {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    max-width: 22rem;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: 0.625rem;
    background: var(--surface-elevated);
  color: var(--content);
    color: inherit;
    text-decoration: none;
}
.lc-filecard:hover { background: var(--surface-sunken); }
.lc-filecard-icon {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 0.5rem;
    background: color-mix(in srgb, var(--accent) 10%, var(--surface-sunken));
    color: var(--accent);
}
.lc-filecard-icon svg { width: 1.25rem; height: 1.25rem; }
.lc-filecard-meta { display: flex; flex-direction: column; min-width: 0; }
.lc-filecard-name {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--content);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.lc-filecard-size { font-size: 0.75rem; color: var(--content-muted); }

/* LC-660: image alt-text control. The badge anchors to the image corner (the
   parent `.lc-img-attach` is the positioning context) and reads as an image
   action, replacing the old floating "Add alt text" link. State: `--missing` is
   a muted amber nudge kept visible so undescribed images stand out; `--set` is a
   filled/checked confirmation revealed on hover/focus to keep the timeline calm. */
.lc-img-attach { position: relative; display: inline-block; max-width: 24rem; }
.lc-alt { position: absolute; top: 0.375rem; right: 0.375rem; }
.lc-alt-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.1875rem;
    padding: 0.125rem 0.375rem;
    border-radius: 9999px;
    border: 1px solid var(--border-strong);
    background: color-mix(in srgb, var(--surface-elevated) 88%, transparent);
    backdrop-filter: blur(2px);
    color: var(--content-muted);
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
    transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.lc-alt-badge::-webkit-details-marker { display: none; }
.lc-alt-badge { list-style: none; }
.lc-alt-ico, .lc-alt-check { width: 0.75rem; height: 0.75rem; flex: none; }
.lc-alt-tag { font-variant: small-caps; }
/* Missing: amber, always visible - the accessibility nudge. */
.lc-alt-badge--missing {
    color: var(--warning-surface-content);
    border-color: var(--warning-border);
    background: color-mix(in srgb, var(--warning-surface) 92%, transparent);
    opacity: 1;
}
.lc-alt-badge--missing:hover,
.lc-alt-badge--missing:focus-visible {
    color: var(--warning-content);
    background: var(--warning);
    border-color: var(--warning);
}
/* Present: filled/checked, revealed on hover/focus of the image or the badge. */
.lc-alt-badge--set {
    color: var(--success-surface-content);
    border-color: var(--success-border);
    background: color-mix(in srgb, var(--success-surface) 92%, transparent);
    opacity: 0;
}
.lc-img-attach:hover .lc-alt-badge--set,
.lc-img-attach:focus-within .lc-alt-badge--set { opacity: 1; }
.lc-alt-badge--set:hover,
.lc-alt-badge--set:focus-visible {
    color: var(--success-content);
    background: var(--success);
    border-color: var(--success);
}
.lc-alt-badge--ro { cursor: default; }
.lc-alt-check { color: inherit; }

/* Popover editor, anchored under the badge. `.lc-alt[open]` reveals it. */
.lc-alt-pop {
    position: absolute;
    top: calc(100% + 0.375rem);
    right: 0;
    z-index: 20;
    width: 18rem;
    max-width: min(18rem, 80vw);
    padding: 0.625rem;
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    background: var(--surface-elevated);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
    cursor: default;
}
.lc-alt-pop-label {
    display: block;
    font-size: 0.6875rem;
    font-weight: 600;
    color: var(--content-muted);
    margin-bottom: 0.3125rem;
}
.lc-alt-pop-input {
    width: 100%;
    resize: vertical;
    min-height: 3.5rem;
    padding: 0.375rem 0.5rem;
    font-size: 0.8125rem;
    color: var(--content);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: 0.375rem;
}
.lc-alt-pop-input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
    border-color: var(--accent);
}
.lc-alt-pop-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-top: 0.4375rem;
}
.lc-alt-count { font-size: 0.6875rem; color: var(--content-subtle); font-variant-numeric: tabular-nums; }
.lc-alt-actions { display: inline-flex; gap: 0.375rem; }
.lc-alt-cancel, .lc-alt-save, .lc-alt-draft {
    padding: 0.25rem 0.625rem;
    font-size: 0.75rem;
    font-weight: 600;
    border-radius: 0.375rem;
    border: 1px solid var(--border);
    cursor: pointer;
}
.lc-alt-cancel { background: var(--surface-elevated); color: var(--content-muted); }
.lc-alt-cancel:hover { background: var(--surface-sunken); color: var(--content); }
/* LC-667: "Auto-draft" (vision alt text). Sits on the left of the actions row;
   hidden unless an operator vision endpoint is configured ([data-lc-vision]
   page root). */
.lc-alt-draft {
    display: none;
    margin-right: auto;
    background: var(--surface-elevated);
    color: var(--accent);
}
[data-lc-vision] .lc-alt-draft { display: inline-flex; }
.lc-alt-draft:hover { background: var(--surface-sunken); }
.lc-alt-draft:disabled { opacity: 0.5; cursor: default; }
.lc-alt-save { background: var(--accent); color: var(--accent-content); border-color: var(--accent); }
.lc-alt-save:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

/* LC-486: inline message translation. The Translate menu item is CSS-gated on
   an LLM being configured - revealed only under a [data-lc-llm] page root (which
   also covers live-swapped message fragments, since the rule is ancestor-based).
   LC-686: the hide rule MUST include `.lc-menu-item` so it is (0,2,0), not (0,1,0).
   The item always carries both classes, and `.lc-menu-item { display: flex }`
   (defined later in this file) is also (0,1,0) - a bare `.lc-llm-only { display:
   none }` loses the source-order tie and never hides, so the item leaked through
   with the flag off. (0,2,0) beats `.lc-menu-item`; the reveal rule below is
   (0,3,0) and still wins. Same cascade trap as LC-678. */
.lc-menu-item.lc-llm-only { display: none; }
[data-lc-llm] .lc-menu-item.lc-llm-only { display: flex; }
/* LC-532: composer AI writing-assistant button, revealed only when an LLM is
   configured (same [data-lc-llm] page-root gate as the translate item). */
.lc-composer-ai { display: none; }
[data-lc-llm] .lc-composer-ai { display: inline-flex; }
/* LC-549: "Find related" menu item, revealed only when an operator embeddings
   endpoint is configured ([data-lc-embeddings] page root, ancestor-based so it
   covers live-swapped message fragments). Independent of the LLM gate.
   LC-686: `.lc-menu-item` prefix so the hide rule is (0,2,0) and actually beats
   `.lc-menu-item { display: flex }` - see the note on `.lc-llm-only` above. */
.lc-menu-item.lc-embeddings-only { display: none; }
[data-lc-embeddings] .lc-menu-item.lc-embeddings-only { display: flex; }
/* LC-549: the room-search "Semantic" toggle, shown only when embeddings are
   configured (same page-root gate). */
.lc-semantic-toggle { display: none; }
[data-lc-embeddings] .lc-semantic-toggle { display: flex; }

/* LC-699: room-header search polish - a clean leading-icon input with a clear
   button + in-flight indicator, a labelled semantic toggle with a hint, and
   richer result rows (author avatar + byline + highlighted snippet). Authored
   here so the styling renders without a Tailwind rebuild. */
.lc-search-field { position: relative; display: block; }
.lc-search-field-icon {
    position: absolute;
    left: 0.5rem;
    top: 1rem;
    transform: translateY(-50%);
    height: 0.875rem;
    width: 0.875rem;
    color: var(--content-subtle);
    pointer-events: none;
}
.lc-search-input {
    width: 100%;
    height: 2rem;
    border-radius: 0.375rem;
    border: 1px solid var(--border);
    background-color: var(--surface-elevated);
    padding-left: 1.75rem;
    padding-right: 1.75rem;
    font-size: 0.75rem;
    line-height: 1rem;
    color: var(--content);
}
.lc-search-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
}
.lc-search-clear {
    position: absolute;
    right: 0.375rem;
    top: 1rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 1.25rem;
    width: 1.25rem;
    border-radius: 9999px;
    color: var(--content-subtle);
}
.lc-search-clear:hover { color: var(--content); background-color: var(--surface-sunken); }
/* Hidden while the field is empty (the placeholder is showing). */
.lc-search-input:placeholder-shown ~ .lc-search-clear { display: none; }
.lc-search-indicator {
    display: none;
    margin-top: 0.375rem;
    font-size: 0.6875rem;
    color: var(--content-muted);
}
.htmx-request.lc-search-indicator { display: block; }

/* Semantic toggle: a labelled row with a short hint, not a bare checkbox. */
.lc-semantic-toggle {
    align-items: flex-start;
    gap: 0.5rem;
    margin-top: 0.5rem;
    cursor: pointer;
}
.lc-semantic-checkbox {
    height: 0.875rem;
    width: 0.875rem;
    margin-top: 0.125rem;
    border-radius: 0.25rem;
    border: 1px solid var(--border);
    accent-color: var(--accent);
    flex: none;
}
.lc-semantic-text { display: flex; flex-direction: column; line-height: 1.2; }
.lc-semantic-label { font-size: 0.75rem; color: var(--content); }
.lc-semantic-hint { font-size: 0.6875rem; color: var(--content-muted); }

/* Results popover. */
.lc-search-panel {
    background-color: var(--surface-elevated);
    border: 1px solid var(--border-strong);
    border-radius: 0.5rem;
    box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.2);
    overflow: hidden;
}
.lc-search-head {
    display: flex;
    align-items: center;
    padding: 0.375rem 0.75rem;
    border-bottom: 1px solid var(--border);
}
.lc-search-scope {
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--content-muted);
}
.lc-search-row {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--border);
}
.lc-search-row:last-child { border-bottom: 0; }
.lc-search-row:hover,
.lc-search-row[aria-selected="true"] { background-color: var(--surface-sunken); }
.lc-search-avatar {
    height: 1.5rem;
    width: 1.5rem;
    border-radius: 9999px;
    object-fit: cover;
    flex: none;
    margin-top: 0.125rem;
}
.lc-search-snippet {
    font-size: 0.8125rem;
    color: var(--content);
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.lc-search-snippet mark {
    background-color: var(--accent-subtle, rgb(250 204 21 / 0.35));
    color: inherit;
    border-radius: 0.125rem;
    padding: 0 0.0625rem;
}
.lc-search-foot {
    padding: 0.375rem 0.75rem;
    border-top: 1px solid var(--border);
    text-align: right;
}
.lc-search-save {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.75rem;
    color: var(--content-muted);
}
.lc-search-save:hover { color: var(--accent); }
/* LC-528: hide the "Read aloud" item when the browser has no SpeechSynthesis.
   live.js sets .lc-no-tts on <html> once at load; the ancestor rule also covers
   live-swapped message fragments. */
.lc-no-tts .lc-read-aloud { display: none; }
/* LC-694: give the translated text its own quiet container - a rounded,
   accent-tinted surface with the accent left border - so it reads as distinct
   from the original message rather than as a plain indented paragraph. */
.lc-translation-block {
    margin-top: 0.375rem;
    border-left: 2px solid color-mix(in srgb, var(--accent) 55%, var(--border));
    border-radius: 0.375rem;
    background: color-mix(in srgb, var(--accent) 5%, var(--surface-sunken));
    padding: 0.375rem 0.625rem;
}
.lc-translation-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
    font-size: 0.6875rem;
    color: var(--content-muted);
}
/* LC-694: the language picker as a proper compact control, matching the app's
   other small inputs. */
.lc-translation-select {
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    background: var(--surface-elevated);
    color: var(--content);
    padding: 0.125rem 1.25rem 0.125rem 0.375rem;
    font-size: 0.6875rem;
    line-height: 1.2;
    cursor: pointer;
}
.lc-translation-select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
}
.lc-translation-hide {
    color: var(--accent);
}
.lc-translation-hide:hover { text-decoration: underline; }
/* LC-694: "Translate" confirm button in the picker-only state - a small accent
   button that reads as the primary action without a heavy full-size button. */
.lc-translation-go {
    border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--border));
    border-radius: 0.375rem;
    background: color-mix(in srgb, var(--accent) 12%, var(--surface-elevated));
    color: var(--accent);
    padding: 0.125rem 0.5rem;
    font-size: 0.6875rem;
    font-weight: 600;
    cursor: pointer;
}
.lc-translation-go:hover {
    background: color-mix(in srgb, var(--accent) 20%, var(--surface-elevated));
}

/* LC-491: event-card header above the RSVP options (poll_block.html). */
.lc-event-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    margin-bottom: 0.25rem;
    border-radius: 0.5rem;
    background: color-mix(in srgb, var(--accent) 8%, var(--surface-elevated));
    border: 1px solid var(--border);
}
.lc-event-head svg { flex: none; width: 1.25rem; height: 1.25rem; color: var(--accent); }
.lc-event-when { font-weight: 600; font-size: 0.875rem; color: var(--content); }
.lc-event-loc { font-size: 0.75rem; color: var(--content-muted); }

/* LC-488: GIF picker result grid (gif_modal.html / gif_results.html). */
.lc-gif-results { max-height: 22rem; overflow-y: auto; }
.lc-gif-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.375rem;
}
.lc-gif-cell {
    border-radius: 0.5rem;
    overflow: hidden;
    background: var(--surface-sunken);
    aspect-ratio: 1 / 1;
    padding: 0;
}
.lc-gif-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lc-gif-cell:hover { outline: 2px solid var(--accent); }
.lc-gif-cell[aria-disabled="true"] { opacity: 0.5; pointer-events: none; }
.lc-gif-empty {
    padding: 1rem;
    text-align: center;
    color: var(--content-muted);
    font-size: 0.875rem;
}
.lc-gif-attribution {
    margin-top: 0.5rem;
    font-size: 0.6875rem;
    color: var(--content-subtle);
    text-align: right;
}

/* LC-506: AI action shown to admins as a disabled teaser (LLM unconfigured). */
.lc-action-disabled { opacity: 0.5; cursor: default; }
.lc-header-action.lc-action-disabled:hover { background-color: transparent; color: var(--content-muted); }
/* LC-511: same teaser posture for the composer GIF button (Giphy unconfigured). */
.lc-composer-btn.lc-action-disabled:hover { background-color: transparent; color: var(--content-muted); }

/* LC-59 server-rendered math. pulldown-latex emits `<math display="...">`
   elements which the browser styles natively (per MathML Core). We do NOT
   set `display: block` on display math: that CSS property overrides the
   browser's native math layout and causes children (the integral sign,
   the integrand, etc.) to stack vertically instead of flowing
   horizontally. The display="block" ATTRIBUTE already makes the element
   a centered block; only add vertical spacing.

   The chat-math subset (`x^2`, sums, integrals, fractions, Greek,
   sub/super, matrices, sqrt) renders correctly under this rule in
   WebKit2GTK (Linux dev-desktop) and should render correctly under
   WebView2 / WKWebView too - both also support MathML Core. Confirmed
   pre-merge per the LC-59 plan's hand-test gate. */
math[display="block"] {
    margin: 0.5rem 0;
}
math {
    vertical-align: -0.15em;
}

/* LC-168: visible highlight for the keyboard-active option in autocomplete
   listboxes (slash-command + mention comboboxes). These set
   aria-selected="true" on the active <li> as the user arrows through, but
   the option's own hover style only triggers on mouse; without this rule the
   keyboard selection is announced to screen readers yet invisible to sighted
   keyboard users. Applies to any role="option" the project marks selected. */
[role="option"][aria-selected="true"] {
    background-color: rgb(241 245 249); /* slate-100 */
}

/* LC-196: motion & micro-interactions.
 *
 * Default posture is opt-in: classes are available but only the global
 * preflight transitions (Tailwind's `transition-colors` on .btn etc., from
 * LC-195) actually run on every element. The classes below are applied where
 * we want a visible enter / loading affordance.
 *
 * All animation is killed under `prefers-reduced-motion: reduce` via the
 * universal reset at the bottom of this block (transitions/animations cut to
 * ~0 duration so JS callbacks bound to `transitionend` / `animationend` still
 * fire). Pattern follows the a11y-project recommendation:
 *   https://www.a11yproject.com/posts/understanding-vestibular-disorders/ */

@keyframes lc-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}
@keyframes lc-scale-in {
    from { opacity: 0; transform: scale(0.97); }
    to   { opacity: 1; transform: scale(1); }
}
@keyframes lc-slide-up {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes lc-shimmer {
    from { background-position: -200% 0; }
    to   { background-position: 200% 0; }
}
@keyframes lc-spin {
    to { transform: rotate(360deg); }
}

/* Opt-in fade for htmx-swapped-in content: add `lc-anim-swap` to any element
   that should fade in after replacement.
 *
 * htmx applies `.htmx-added` to swapped-in content during the settle phase
 * and removes it after `config.defaultSettleDelay` (default 20ms). We use a
 * *transition* (not an animation): the swapped-in element starts at
 * `opacity: 0` while `.htmx-added` matches, then transitions to `opacity: 1`
 * once it's removed. A keyframe animation bound to `.htmx-added` would be
 * cancelled at 20ms before the fade could finish; this is the canonical
 * htmx fade-in pattern (see htmx docs section "Animations"). */
.lc-anim-swap {
    opacity: 1;
    transition: opacity 180ms ease-out;
}
.lc-anim-swap.htmx-added {
    opacity: 0;
}

/* LC-246: brief highlight when arriving at a message via a `/m/{id}` permalink.
   `partials/auto_scroll.html` adds `.lc-msg-flash` to the target row on load and
   removes it after the animation. Theme-aware via `--accent-surface`. */
@keyframes lc-msg-flash {
    0%   { background-color: var(--accent-surface); }
    100% { background-color: transparent; }
}
.lc-msg-flash {
    animation: lc-msg-flash 2s ease-out;
    border-radius: 0.25rem;
}
@media (prefers-reduced-motion: reduce) {
    .lc-msg-flash { animation: none; }
}

/* LC-256: long-message fold. `partials/auto_scroll.html` adds `.lc-collapsible`
   to a message body that overflows the threshold (and removes it on expand).
   The bottom fade hints that more content is hidden; it fades to `--surface`
   (the message-list background) and is non-interactive so the toggle below
   stays clickable. */
.lc-collapsible {
    max-height: 20rem;
    overflow: hidden;
    position: relative;
}
.lc-collapsible::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 4rem;
    /* Smoother two-stop fade so the cut reads as an intentional fade, not a hard
       crop. Fades to --surface (the message-list background). */
    background: linear-gradient(to bottom, transparent, var(--surface) 85%);
    pointer-events: none;
}

/* LC-386: jump-to-date custom month-grid calendar (built by auto_scroll.html),
   replacing the native date input's mis-positioning browser picker. */
.lc-jd-nav {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 1.5rem;
    width: 1.5rem;
    border-radius: 0.375rem;
    color: var(--content-muted);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}
.lc-jd-nav:hover:not(:disabled) {
    background-color: var(--surface-sunken);
    color: var(--content);
}
.lc-jd-nav:disabled {
    opacity: 0.35;
    cursor: default;
}
.lc-jd-dow {
    font-size: 0.625rem;
    font-weight: 600;
    color: var(--content-subtle);
    padding: 0.125rem 0;
    text-transform: uppercase;
}
.lc-jd-day {
    height: 1.875rem;
    border-radius: 0.375rem;
    font-size: 0.75rem;
    color: var(--content);
    cursor: pointer;
}
.lc-jd-day:hover:not(:disabled) {
    background-color: var(--surface-sunken);
}
.lc-jd-day:disabled {
    opacity: 0.3;
    cursor: default;
}
.lc-jd-today {
    font-weight: 600;
    color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
}
.lc-jd-btn {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--content-muted);
    padding: 0.25rem 0.5rem;
    border-radius: 0.375rem;
    cursor: pointer;
}
.lc-jd-btn:hover {
    background-color: var(--surface-sunken);
    color: var(--content);
}

/* LC-262: image lightbox. The full image is constrained to the viewport and
   letterboxed; sizing lives here (not Tailwind arbitrary values) so it is
   reliable without a CSS rebuild. The modal shell is lightbox_modal.html. */
.lc-lightbox-img {
    max-width: 90vw;
    max-height: 85vh;
    object-fit: contain;
}

/* LC-264: pinned-strip collapse chevron. Points down when expanded, rotates to
   point right when the strip is collapsed (toggle aria-expanded=false). */
[data-lc-pin-chevron] {
    transition: transform 0.15s ease;
}
[data-lc-pin-toggle][aria-expanded="false"] [data-lc-pin-chevron] {
    transform: rotate(-90deg);
}

/* LC-560 (Blue Harbor): the room sidebar is dark-navy chrome in every theme. It
   is wired by REDEFINING the semantic tokens on the container to the --sidebar-*
   family (LC-554 phase 1b), so every descendant utility (bg-surface-sunken,
   text-content, border-border, hover:bg-surface-elevated, the .lc-* component
   classes) recolors to navy automatically - no markup change inside the OOB-
   swapped fragments, which keeps every hx-swap-oob target id + structure intact.
   The navy ramp maps naturally: surface = base column, elevated = hover/menus,
   sunken = insets. --accent is intentionally NOT overridden so accent fills
   (unread badges, pressed toggles, the active-row left bar) keep the brand blue;
   only --accent-surface flips to a raised navy so the active row reads as a
   quiet lift, not a bright light-blue chip. */
.lc-sidebar {
    --surface:          var(--sidebar-surface);
    --surface-elevated: var(--sidebar-elevated);
    --surface-sunken:   var(--sidebar-sunken);
    --content:          var(--sidebar-content);
    --content-muted:    var(--sidebar-muted);
    --content-subtle:   var(--sidebar-muted);
    --border:           var(--sidebar-border);
    --border-strong:    var(--sidebar-border);
    --accent-surface:         var(--sidebar-elevated);
    --accent-surface-content: var(--sidebar-content);
    background-color: var(--sidebar-surface);
    color: var(--sidebar-content);
}
/* LC-569: the active row is now a solid --accent fill, so its label takes
   --accent-content (which is not remapped inside .lc-sidebar) and reads on the
   accent. The old override forced --sidebar-content for the accent-text-on-navy
   treatment that the solid fill replaces. */
/* The search input has no explicit placeholder color; default browser placeholder
   is too faint on navy, so pin it to the muted sidebar token. */
.lc-sidebar input::placeholder {
    color: var(--sidebar-muted);
}

/* LC-576: the right column (prototype .rightcol) - ONE rail that owns the single
   border and the scroll, holding the thread / history / details panels as
   subcards (.lc-panel, prototype .panel: 14px margin, 12px radius, elevated on
   the column's plain surface). Previously each panel was its own full-bleed
   aside with its own border-l, so an open thread read as a second rail bolted
   onto the details rail instead of one rail of cards. */
.lc-rightcol {
    width: 22.5rem;
    flex: 0 0 22.5rem;
    border-left: 1px solid var(--border);
    background-color: var(--surface);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
}
.lc-panel {
    margin: 0.875rem;
    flex: none;
    border: 1px solid var(--border);
    border-radius: 0.75rem;
    background-color: var(--surface-elevated);
    overflow: hidden;
}
/* The thread panel scrolls its own replies list, so it must be able to shrink
   inside the column rather than growing the column's scroll height forever. */
.lc-rightcol > .lc-panel.flex { min-height: 0; max-height: calc(100vh - 8rem); }

/* LC-576: sidebar head - product title + collapse chevron, matching the
   prototype's .sb-head / .sb-title (16px 16px 10px, 18px/700). */
.lc-sidebar-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: none;
    padding: 1rem 1rem 0.625rem;
}
.lc-sidebar-title {
    font-size: 1.125rem;
    font-weight: 700;
    white-space: nowrap;
    color: var(--content);
}

/* LC-576: org row + its enclave menu (prototype .sb-org: 6px 16px 12px, 15px/600,
   muted chevron). A native <details> so it needs no JS; the summary marker is
   stripped because the chevron is drawn explicitly. */
.lc-sidebar-org-wrap {
    position: relative;
    flex: none;
}
.lc-sidebar-org {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.375rem 1rem 0.75rem;
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--content);
    cursor: pointer;
    list-style: none;
}
.lc-sidebar-org::-webkit-details-marker { display: none; }
.lc-sidebar-org-chev {
    height: 1rem;
    width: 1rem;
    flex: none;
    color: var(--content-muted);
    transition: transform 0.15s ease;
}
.lc-sidebar-org-wrap[open] .lc-sidebar-org-chev { transform: rotate(180deg); }
.lc-sidebar-org-menu {
    position: absolute;
    left: 0.75rem;
    right: 0.75rem;
    top: 100%;
    z-index: 40;
    padding: 0.375rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    background-color: var(--surface-elevated);
    box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.2);
}

/* LC-576: search tabs (prototype .sb-tabs / .sb-tab: two evenly split tabs on the
   sunken ramp, 12px/600, 7px radius - no bordered shell around them). */
.lc-sidebar-tabs {
    display: flex;
    gap: 0.25rem;
    padding: 0 1rem 0.625rem;
}
.lc-sidebar-tab {
    flex: 1;
    text-align: center;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.3125rem 0;
    border-radius: 0.4375rem;
    color: var(--content-muted);
    background-color: var(--surface-sunken);
    cursor: pointer;
}
.lc-sidebar-tab:hover { color: var(--content); }
.lc-sidebar-tab[aria-pressed="true"] {
    background-color: var(--accent);
    color: var(--accent-content);
}

/* LC-576: search row (prototype .sb-search) - the field and the "new room"
   button share one row. */
.lc-sidebar-search {
    display: flex;
    gap: 0.5rem;
    padding: 0 1rem 0.875rem;
}
.lc-sidebar-field {
    width: 100%;
    min-width: 0;
    background-color: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    padding: 0.4375rem 0.625rem;
    font-size: 0.8125rem;
    color: var(--content);
}
.lc-sidebar-field:focus {
    outline: none;
    border-color: var(--accent);
}
/* LC-576: ⌘K hint chip inside the search field (prototype .sb-search kbd). The
   field reserves right padding so typed text never slides under the chip. */
.lc-sidebar-search .lc-sidebar-field { padding-right: 3.25rem; padding-left: 1.9rem; }
/* LC-553: ephemeral-timer (self-destruct) control - a clock-icon popover that
   replaces the raw <select>. Muted clock when off; accent clock + inline
   duration when a timer is armed. Menu opens upward (composer is at the foot). */
.lc-ttl { position: relative; align-self: center; }
.lc-ttl > summary { list-style: none; cursor: pointer; }
.lc-ttl > summary::-webkit-details-marker { display: none; }
.lc-ttl > summary::marker { content: ""; }
.lc-ttl-summary {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    height: 1.875rem;
    padding: 0 0.4rem;
    border-radius: 0.5rem;
    color: var(--content-muted);
}
.lc-ttl-summary:hover { background-color: var(--surface-sunken); color: var(--content); }
.lc-ttl-summary svg { height: 1.05rem; width: 1.05rem; flex: none; }
.lc-ttl-val { font-size: 0.6875rem; font-weight: 700; line-height: 1; }
.lc-ttl.lc-ttl-armed .lc-ttl-summary { color: var(--accent); }
.lc-ttl-menu {
    position: absolute;
    right: 0;
    bottom: calc(100% + 0.375rem);
    z-index: 40;
    min-width: 8.5rem;
    padding: 0.25rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    background-color: var(--surface-elevated);
    box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.2);
}
.lc-ttl-opt {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.5rem;
    border-radius: 0.375rem;
    font-size: 0.8125rem;
    color: var(--content);
    cursor: pointer;
}
.lc-ttl-opt:hover { background-color: var(--surface-sunken); }
.lc-ttl-opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.lc-ttl-opt::before {
    content: "";
    width: 1rem;
    height: 1rem;
    flex: none;
    border-radius: 999px;
    border: 1.5px solid var(--border-strong);
}
.lc-ttl-opt:has(:checked) { color: var(--accent); font-weight: 600; }
.lc-ttl-opt:has(:checked)::before {
    border-color: var(--accent);
    background: var(--accent);
    box-shadow: inset 0 0 0 3px var(--surface-elevated);
}

/* LC-553: room-header member avatar stack (prototype) - overlapping round
   avatars with a "+N" overflow count. */
.lc-member-stack {
    display: inline-flex;
    align-items: center;
    flex: none;
    margin-right: 0.25rem;
    /* LC-683: now a <button> (opens the members panel) rather than an <a>; reset
       the UA button chrome so it reads as the bare avatar stack it did before. */
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    color: inherit;
    font: inherit;
}
.lc-member-av {
    height: 1.75rem;
    width: 1.75rem;
    border-radius: 999px;
    object-fit: cover;
    border: 2px solid var(--surface);
    margin-left: -0.5rem;
    background-color: var(--surface-sunken);
}
.lc-member-av:first-child { margin-left: 0; }
.lc-member-more {
    margin-left: 0.4rem;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--content-muted);
    white-space: nowrap;
}

.lc-sb-search-icon {
    position: absolute;
    left: 0.5625rem;
    top: 50%;
    transform: translateY(-50%);
    height: 0.9rem;
    width: 0.9rem;
    color: var(--content-subtle);
    pointer-events: none;
}
.lc-sb-kbd {
    position: absolute;
    right: 0.5rem;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    font-family: inherit;
    font-size: 0.6875rem;
    line-height: 1;
    color: var(--content-subtle);
    background-color: var(--surface-elevated);
    border: 1px solid var(--border);
    border-radius: 0.3125rem;
    padding: 0.1875rem 0.3125rem;
}
.lc-sidebar-add {
    height: 2.125rem;
    width: 2.125rem;
    flex: 0 0 2.125rem;
    border-radius: 0.5rem;
    background-color: var(--accent);
    color: var(--accent-content);
    display: grid;
    place-items: center;
    cursor: pointer;
}
.lc-sidebar-add:hover { background-color: var(--accent-hover); }
.lc-sidebar-add svg { height: 1.125rem; width: 1.125rem; }

/* LC-576: the bottom-left utility row (Profile / Shortcuts / Settings), matching
   the prototype's .sb-foot. It sits inside .lc-sidebar, which already remaps the
   generic tokens onto the sidebar ramp, so plain var(--border) / var(--content)
   here resolve to the navy variants - no sidebar-specific tokens needed. */
.lc-sidebar-foot {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    flex: none;
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--border);
    color: var(--content-muted);
    font-size: 0.875rem;
    cursor: pointer;
}
.lc-sidebar-foot:hover { color: var(--content); }
.lc-sidebar-foot svg { height: 1.125rem; width: 1.125rem; flex: none; }


/* LC-576: collapsed create-category disclosure at the foot of the room list. */
.lc-sb-newcat {
    margin-top: 0.5rem;
    padding: 0.5rem 0.5rem 0;
    border-top: 1px solid var(--border);
}
.lc-sb-newcat > summary {
    list-style: none;
    cursor: pointer;
    padding: 0.125rem 0;
}
.lc-sb-newcat > summary::-webkit-details-marker { display: none; }
.lc-sb-newcat > summary:hover { color: var(--content); }

/* LC-576: rail utilities at the foot (prototype: gear, help, avatar stacked). */
.lc-rail-util {
    display: grid;
    place-items: center;
    height: 2.25rem;
    width: 2.25rem;
    flex: none;
    border-radius: 0.625rem;
    color: var(--rail-content-muted);
    cursor: pointer;
}
.lc-rail-util:hover {
    background-color: var(--rail-tile);
    color: var(--rail-content);
}
.lc-rail-util svg { height: 1.125rem; width: 1.125rem; }
/* LC-576: the profile control in the rail foot. partials/sidebar_self.html is
   reused verbatim (so the account menu + the own_profile_live OOB swap keep
   working); these rules collapse its trigger to the avatar alone and fly the
   popovers out to the RIGHT of the 64px rail, where they have room. */
.lc-rail-self {
    flex: none;
    margin-bottom: 0.25rem;
}
/* Trigger: avatar only - drop the name / @handle / status text column and the
   chevron. Target the TEXT column (span.min-w-0) explicitly: avatar.html's own
   root is `<span class="relative inline-block shrink-0">`, which carries no
   `rounded-full`, so a negative selector like :not([class*="rounded-full"])
   matches the AVATAR and hides it - which is exactly what it did. */
.lc-rail-self #own-avatar-wrapper {
    width: auto;
    padding: 0.125rem;
    gap: 0;
    border-radius: 9999px;
}
.lc-rail-self #own-avatar-wrapper > span.min-w-0,
.lc-rail-self [data-lc-account-chevron] {
    display: none;
}
.lc-rail-self #own-avatar-wrapper:hover { background-color: var(--rail-tile); }
/* Menu: out to the right, anchored to the rail foot rather than below the
   trigger (there is no room below - it is the last thing in the rail). */
.lc-rail-self #lc-account-menu {
    left: calc(100% + 0.5rem);
    right: auto;
    top: auto;
    bottom: 0;
    margin-top: 0;
    width: 15rem;
}
/* LC-553: rail settings dropdown (native <details>). The flyout escapes the rail
   to the right, same treatment as the account menu; the disclosure triangle is
   suppressed so the summary reads as the plain gear tile. */
.lc-rail-menu { position: relative; flex: none; }
.lc-rail-menu > summary { list-style: none; cursor: pointer; }
.lc-rail-menu > summary::-webkit-details-marker { display: none; }
.lc-rail-menu > summary::marker { content: ""; }
.lc-rail-flyout {
    position: absolute;
    left: 3.25rem;
    right: auto;
    bottom: 0;
    z-index: 50;
    width: 13rem;
    padding: 0.375rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    background-color: var(--surface-elevated);
    box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.2);
}

/* LC-553: rail avatar hovercard. The name / @handle / status column is hidden in
   the rail (avatar-only trigger); on hover reveal it as a two-line card flying
   out to the right, so identity is discoverable without opening the menu. */
.lc-rail-self:hover #own-avatar-wrapper > span.min-w-0 {
    display: block;
    position: absolute;
    left: 3.25rem;
    right: auto;
    bottom: 0;
    z-index: 55;
    width: 12rem;
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    background-color: var(--surface-elevated);
    color: var(--content);
    box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.2);
    text-align: left;
    white-space: normal;
    pointer-events: none;
}
/* LC-553: once the account menu is OPEN (avatar clicked -> aria-expanded), the
   menu already carries the name/@handle in its own header, so suppress the hover
   card - otherwise it fires on top of the open menu as a duplicate identity. */
.lc-rail-self:hover #own-avatar-wrapper[aria-expanded="true"] > span.min-w-0 { display: none; }

/* The status picker swaps in here; same fly-out treatment. */
.lc-rail-statuspicker {
    position: relative;
    flex: none;
}
.lc-rail-statuspicker:not(:empty) > * {
    position: absolute;
    left: calc(100% + 0.5rem);
    bottom: 0;
    z-index: 50;
    width: 15rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    background-color: var(--surface-elevated);
    box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.2);
}

/* LC-270: collapse the room sidebar to the enclave rail on desktop. The wide
   #sidebar hides; the w-16 enclave switcher stays as the rail. Desktop only:
   on mobile the sidebar is an overlay with its own toggle (lcOpenNav), so the
   collapse preference must not hide it there. The toggle chevron points left
   (collapse) when expanded and flips to point right (expand) when collapsed. */
@media (min-width: 768px) {
    html[data-lc-sidebar="collapsed"] #sidebar {
        display: none;
    }
}
/* LC-553: the expand ribbon. Once the sidebar is hidden this half-round tab on
   the rail's outer seam is the way back - the sidebar no longer just vanishes
   "into" the rail behind another rail icon. It exists ONLY in the collapsed
   state (and only on desktop, where the collapse applies at all); expanded, the
   chevron next to the sidebar title is the control. Sits at the rail's width
   (w-16 = 4rem) so it reads as a tab pulling the sidebar back out. */
.lc-sidebar-ribbon {
    display: none;
    position: fixed;
    left: 4rem;
    top: 50%;
    transform: translateY(-50%);
    z-index: 25;
    align-items: center;
    justify-content: center;
    width: 1.15rem;
    height: 2.75rem;
    padding: 0;
    /* half-round on the outer edge only, so it reads as a tab on the seam */
    border-radius: 0 999px 999px 0;
    border: 1px solid var(--sidebar-border);
    border-left: 0;
    background: var(--rail-surface);
    color: var(--rail-content-muted);
    box-shadow: 2px 0 6px -2px rgb(0 0 0 / 0.35);
    transition: width 120ms ease, color 120ms ease, background-color 120ms ease;
}
@media (min-width: 768px) {
    html[data-lc-sidebar="collapsed"] .lc-sidebar-ribbon {
        display: inline-flex;
    }
}
.lc-sidebar-ribbon:hover {
    width: 1.45rem;
    background: var(--rail-tile);
    color: var(--rail-content);
}
.lc-sidebar-ribbon:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--ring);
}
.lc-sidebar-ribbon svg {
    flex: none;
}

/* LC-576: mirror of the above for the right Details panel, so both rails have a
   collapse chevron. Desktop only, for the same reason the left one is: below
   768px the right column already stacks/hides, and a stored collapse
   preference must not fight that. The chevron points right (collapse, ">>")
   when the panel is open and flips to point left ("<<") when it is closed. */
/* LC-553: the right column holds Details (toggled by the header info icon and
   closed by its own X) and Thread (opened from a message, closed by its X).
   Each panel hides independently; the whole column collapses to nothing when
   BOTH are closed, so no separate collapse control is needed. */
html[data-lc-details="collapsed"] #details-panel { display: none; }
@media (min-width: 768px) {
    /* Details closed AND thread not open -> drop the now-empty column. */
    html[data-lc-details="collapsed"] #rightcol:has(#thread-panel.hidden) { display: none; }
}
/* LC-553: the shared emoji picker (w-72) is left-anchored for the wide composer;
   in the narrow thread reply it would overflow the panel, so right-anchor it. */
#lc-thread-emoji-slot [data-lc-emoji-picker-panel] { left: auto; right: 0; }

/* Shared close (X) button for the Details and Thread panel headers. */
.lc-panel-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 1.75rem;
    width: 1.75rem;
    flex: none;
    border-radius: 0.5rem;
    color: var(--content-muted);
    cursor: pointer;
}
.lc-panel-close:hover { color: var(--content); background-color: var(--surface-sunken); }

/* Skeleton block (for slow loads). Apply `.lc-skeleton` to a sized element
   while content streams in. */
.lc-skeleton {
    background: linear-gradient(
        90deg,
        var(--surface-sunken) 0%,
        var(--border) 50%,
        var(--surface-sunken) 100%
    );
    background-size: 200% 100%;
    animation: lc-shimmer 1.4s linear infinite;
    border-radius: 0.25rem;
    color: transparent;
}

/* `hx-indicator` partner: an inline spinner that becomes visible while the
   bound request is in flight. Wired via htmx's built-in `.htmx-indicator`
   class: hidden by default, opacity:1 once the parent gains `.htmx-request`.
   Pair with the component variant `.lc-spinner` for the rotating glyph. */
.lc-spinner {
    display: inline-block;
    width: 0.9em;
    height: 0.9em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 9999px;
    animation: lc-spin 0.7s linear infinite;
    vertical-align: -0.125em;
}

/* LC-650: uniform "AI is working" pending block. Injected by live.js into an
   AI action's htmx target while the (possibly slow, CPU-bound) LLM request is
   in flight, then replaced by the real result on completion. Hand-authored here
   (not a Tailwind utility) so it renders regardless of the JIT purge. */
.lc-ai-pending {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.125rem;
    font-size: 0.8125rem;
    line-height: 1.2;
    color: var(--content-muted);
}

/* LC-654: summary-shaped skeleton + staged status for the AI summary panels.
   Previewing the output's shape (heading, a few lines, a short action-items
   cluster) reads as "content is coming" far better than a bare spinner while
   the local model works. */
.lc-ai-skel {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    padding: 0.25rem 0;
}
.lc-ai-skel-status {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8125rem;
    color: var(--content-muted);
}
.lc-skel-block {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}
.lc-skel-bar {
    height: 0.62rem;
    border-radius: 0.375rem;
    background: var(--surface-sunken);
    position: relative;
    overflow: hidden;
}
.lc-skel-gap {
    margin-top: 0.6rem;
}
/* A lighter band (surface-elevated over the sunken bar) sweeps across - subtle
   and theme-aware without color-mix. */
.lc-skel-bar::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, var(--surface-elevated), transparent);
    opacity: 0.7;
    animation: lc-skel-shimmer 1.5s ease-in-out infinite;
}
@keyframes lc-skel-shimmer {
    to { transform: translateX(100%); }
}

/* LC-654: inline error + retry for the summary surfaces (never an endless
   spinner). */
.lc-ai-error {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.8125rem;
    color: var(--content-muted);
}
.lc-ai-error-msg {
    min-width: 0;
}
.lc-ai-retry {
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    padding: 0.15rem 0.5rem;
    background: var(--surface-elevated);
    color: var(--content-muted);
}
.lc-ai-retry:hover {
    background: var(--surface-sunken);
    color: var(--content);
}

/* LC-654: the summary enters from the skeleton smoothly, not a jarring pop. */
.lc-ai-enter {
    animation: lc-ai-fade-in 220ms ease-out;
}
@keyframes lc-ai-fade-in {
    from { opacity: 0; transform: translateY(3px); }
    to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    /* No shimmer sweep or slide-in; a gentle opacity pulse keeps it "alive". */
    .lc-skel-bar::after { animation: none; opacity: 0; }
    .lc-skel-bar { animation: lc-skel-pulse 1.8s ease-in-out infinite; }
    .lc-ai-enter { animation: none; }
}
@keyframes lc-skel-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.55; }
}

/* LC-655: the sparkle writing-assistant mode menu (a <details> popover opening
   upward from the composer toolbar). */
.lc-ai-menu > summary { list-style: none; cursor: pointer; }
.lc-ai-menu > summary::-webkit-details-marker { display: none; }
.lc-ai-menu-pop {
    position: absolute;
    bottom: 100%;
    left: 0;
    margin-bottom: 0.375rem;
    z-index: 30;
    min-width: 12rem;
    padding: 0.25rem;
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    background: var(--surface-elevated);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
.lc-ai-menu-head {
    padding: 0.25rem 0.5rem 0.375rem;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--content-subtle);
}
.lc-ai-menu-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 0.375rem 0.5rem;
    border-radius: 0.375rem;
    font-size: 0.8125rem;
    color: var(--content);
}
.lc-ai-menu-item:hover { background: var(--surface-sunken); }
/* LC-669: divider between the rewrite modes and the read-only "Check tone". */
.lc-ai-menu-sep {
    height: 1px;
    margin: 0.25rem 0.25rem;
    background: var(--border);
}

/* LC-655: "Applied - Undo" affordance after accepting a rewrite (the applied
   text stays editable; Undo restores the original draft). */
.lc-ai-applied {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.5rem;
    padding: 0.375rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    background: var(--surface-elevated);
    font-size: 0.8125rem;
    color: var(--content-muted);
}
.lc-ai-applied-msg { min-width: 0; flex: 1; }

/* Toast / modal entry. The component classes in tailwind.css apply these by
   default; reduced-motion strips them via the block below. */
.toast {
    animation: lc-slide-up 180ms ease-out;
}
.modal-panel {
    animation: lc-scale-in 140ms ease-out;
}
.modal-backdrop {
    animation: lc-fade-in 120ms ease-out;
}

/* LC-199: iOS safe-area helpers. With the LC-199 `viewport-fit=cover` meta,
 * fixed bars + the composer extend behind the notch / home indicator; these
 * classes restore the gap. `max(...)` keeps the existing visual padding when
 * the inset is 0 (desktop / non-cutout devices). */
.lc-pb-safe {
    padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
}
.lc-px-safe {
    padding-left: max(0px, env(safe-area-inset-left));
    padding-right: max(0px, env(safe-area-inset-right));
}
/* For a `bottom: 0`-anchored bar (toast / outbox / call dock) push it up by
 * the inset instead of relying on inner padding. */
.lc-bottom-safe {
    bottom: max(0.75rem, calc(0.75rem + env(safe-area-inset-bottom)));
}

/* LC-199: nav drawer transition. The desktop layout is `md:static md:flex`
 * (no transform), so this rule applies only at mobile widths. The JS toggles
 * the `hidden` class as before; we also key off a `data-open` attribute so
 * the drawer slides in/out instead of snapping. Falling back to the binary
 * `hidden` toggle if JS doesn't set data-open keeps the worst case correct. */
@media (max-width: 767px) {
    #lc-nav-panel {
        transform: translateX(-100%);
        transition: transform 200ms ease-out;
    }
    #lc-nav-panel[data-open="true"] {
        transform: translateX(0);
    }
    #lc-nav-backdrop {
        opacity: 0;
        transition: opacity 200ms ease-out;
    }
    #lc-nav-backdrop[data-open="true"] {
        opacity: 1;
    }
}

/* LC-365: typography scale. Semantic classes that pair font-size + weight +
   line-height (+ tracking) so the chrome stops hand-rolling `text-2xl
   font-semibold` combos and every heading/label is consistent. Defined as
   plain CSS here (not Tailwind `@layer`) so the classes are always present for
   the redesign work and never purged. Sizes mirror Tailwind's rem scale so
   they read familiarly. Color is left to the caller (text-content /
   text-content-muted) EXCEPT caption/section-label, which imply a muted tone.
   Spacing rhythm stays on Tailwind's 4px scale - see docs/ui-conventions.md. */
.lc-display { font-size: 1.5rem;    font-weight: 600; line-height: 1.2;  letter-spacing: -0.01em; }
/* LC-576: 700 to match the prototype's page/room titles (was 600). */
.lc-h1      { font-size: 1.125rem;  font-weight: 700; line-height: 1.35; }
.lc-h2      { font-size: 1rem;      font-weight: 600; line-height: 1.4; }
.lc-h3      { font-size: 0.875rem;  font-weight: 600; line-height: 1.4; }
.lc-body    { font-size: 0.875rem;  font-weight: 400; line-height: 1.5; }
.lc-small   { font-size: 0.75rem;   font-weight: 400; line-height: 1.45; }
.lc-caption { font-size: 0.75rem;   font-weight: 400; line-height: 1.35; color: var(--content-muted); }
/* LC-558: message-header timestamp. Sits next to the (now font-semibold) author
   name; smaller + one step subtler than .lc-caption so the "who / when" split
   reads cleanly. tabular-nums keeps HH:MM steady. Separate from .lc-caption,
   which is shared by other surfaces. */
.lc-msg-time { font-size: 0.6875rem; font-weight: 400; line-height: 1.35; color: var(--content-subtle); font-variant-numeric: tabular-nums; }
/* Uppercase muted group label (sidebar section headers, Slack/Discord style). */
.lc-section-label {
    font-size: 0.6875rem;
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--content-subtle);
}

/* LC-370: shared tooltip element. Positioned (fixed) by tooltip.js so it
   escapes overflow:auto ancestors like the enclave rail. Token-driven, so it
   inverts with the theme (dark chip on light themes, light chip on dark). The
   trigger's aria-label remains the accessible name; this element is visual only
   (aria-hidden in the JS). */
#lc-tooltip {
    position: fixed;
    z-index: 80;
    display: none;
    padding: 0.25rem 0.5rem;
    border-radius: 0.375rem;
    background-color: var(--content);
    color: var(--surface-elevated);
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.3;
    /* LC-651: wrap within a bounded width. Short labels stay one line; a long
       reaction "who reacted" list (up to 20 names) wraps instead of running off
       the viewport, which the old `nowrap` + no-max-width would have done. */
    white-space: normal;
    max-width: min(18rem, 90vw);
    text-align: center;
    pointer-events: none;
    box-shadow: 0 4px 12px rgb(0 0 0 / 0.18);
    opacity: 0;
    transition: opacity 0.1s ease-out;
}
#lc-tooltip.lc-tooltip-visible {
    opacity: 1;
}

/* LC-366: enclave rail. Discord/Slack-style vertical app rail driven by the
   --rail-* tokens (theme-aware, no hardcoded slate-*). Each tile is a rounded
   square at rest that morphs toward a circle on hover/active, with a white
   indicator pill in the left gutter that grows on hover and is tallest on the
   active tile. The pill lives in the rail's left gutter, so it needs the rail
   itself (not the scrolling tile column) as its visual anchor. */
#switcher {
    background-color: var(--rail-surface);
    /* Seam so the rail reads as distinct chrome from the sidebar even when both
       are near-black (every dark/hc state, identical #000 in hc). LC contrast fix. */
    border-right: 1px solid var(--sidebar-border);
}
.lc-rail-tile {
    position: relative;
    flex: none;
    background-color: var(--rail-tile);
    color: var(--rail-content);
    border-radius: 0.95rem;
    transition: border-radius 0.15s ease, background-color 0.15s ease;
}
.lc-rail-tile:hover {
    background-color: var(--rail-tile-hover);
    border-radius: 1.375rem;
}
/* LC-576: the active tile RECOLORS but keeps its rounded-square shape - it used
   to also morph to a circle, so selecting an enclave changed its silhouette. The
   circle is a hover affordance only (see :hover above), which still applies to
   the active tile on hover. */
.lc-rail-tile.lc-rail-active {
    background-color: var(--accent);
    color: var(--accent-content);
}
/* Left indicator pill (Discord pattern). Sits in the rail gutter to the left of
   the tile: 0 height at rest, a short stub on hover, tallest when active. */
.lc-rail-tile::before {
    content: "";
    position: absolute;
    left: -0.625rem;
    top: 50%;
    width: 0.25rem;
    height: 0;
    border-radius: 0 0.25rem 0.25rem 0;
    background-color: var(--rail-content);
    transform: translateY(-50%);
    transition: height 0.15s ease;
}
.lc-rail-tile:hover::before {
    height: 1.25rem;
}
.lc-rail-tile.lc-rail-active::before {
    height: 2.25rem;
}



/* LC-576: the enclave stack is the rail's only scroll region, so the foot
   utilities (settings / shortcuts / avatar) stay pinned and reachable no matter
   how many enclaves the user is in. flex:1 + min-height:0 is what lets it
   actually shrink inside the flex column rather than overflowing it. */
.lc-rail-scroll {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    overflow-y: auto;
}

/* LC-576: the rail's discover/create `+` - a bare action glyph with no tile fill,
   so it does not read as another enclave in the stack. */
.lc-rail-add {
    flex: none;
    color: var(--rail-content-muted);
    border-radius: 0.95rem;
    transition: color 0.15s ease, background-color 0.15s ease;
}
.lc-rail-add:hover {
    color: var(--rail-content);
    background-color: var(--rail-tile);
}

/* Settings gear on the active enclave tile + the collapse chevron: muted rail
   icons that brighten on hover. */
.lc-rail-gear {
    color: var(--rail-content-muted);
    transition: color 0.15s ease, background-color 0.15s ease;
}
.lc-rail-gear:hover {
    color: var(--rail-content);
    background-color: var(--rail-tile);
}
/* Thin divider above the discover / collapse controls at the rail foot. */
.lc-rail-divider {
    flex: none;
    width: 2rem;
    height: 1px;
    background-color: var(--rail-tile-hover);
}
/* Unread / mention badge: a ring in the rail surface color separates it from a
   neighboring tile. */
.lc-rail-badge {
    position: absolute;
    top: -0.25rem;
    right: -0.25rem;
    min-width: 1.1rem;
    height: 1.1rem;
    padding: 0 0.3rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 9999px;
    /* LC-576: the enclave-rail unread badge is accent (matching the comp), not
       danger-red - it counts unread, it is not an error. */
    background-color: var(--accent);
    color: var(--accent-content);
    font-size: 0.6875rem;
    font-weight: 600;
    line-height: 1;
    box-shadow: 0 0 0 2px var(--rail-surface);
}

/* LC-367: sidebar utility / navigation rows. One consistent icon+label row
   rhythm for the grouped "Navigation" and "Account" sections (Slack/Discord
   left-nav convention), replacing the old flat icon-less link list. The icon
   inherits the row's text color (fill="currentColor"), so it dims at rest and
   brightens with the label on hover. */
.lc-nav-row {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    width: 100%;
    padding: 0.375rem 0.5rem;
    border-radius: 0.375rem;
    font-size: 0.875rem;
    line-height: 1.25rem;
    text-align: left;
    color: var(--content-muted);
}
.lc-nav-row:hover {
    background-color: var(--surface-elevated);
    color: var(--content);
}
.lc-nav-row svg {
    width: 1rem;
    height: 1rem;
    flex: none;
}
/* Role-gated admin / moderate row: danger-toned so it reads as privileged. */
.lc-nav-row-danger {
    color: var(--danger);
}
.lc-nav-row-danger:hover {
    color: var(--danger);
}

/* LC-368: account dropdown menu items. Like .lc-nav-row but tuned for a menu on
   an elevated surface: full-strength text at rest, a sunken hover/focus tint
   (the row sits on --surface-elevated, so hover must go the other way). The
   icon is muted relative to the label. focus-visible mirrors hover for keyboard
   menu navigation. */
.lc-menu-item {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    width: 100%;
    padding: 0.375rem 0.5rem;
    border-radius: 0.375rem;
    font-size: 0.875rem;
    line-height: 1.25rem;
    text-align: left;
    color: var(--content);
}
.lc-menu-item:hover,
.lc-menu-item:focus-visible {
    background-color: var(--surface-sunken);
    outline: none;
}
.lc-menu-item svg {
    width: 1rem;
    height: 1rem;
    flex: none;
    color: var(--content-muted);
}
.lc-menu-item-danger,
.lc-menu-item-danger svg {
    color: var(--danger);
}
/* Thin divider inside a menu (separates destructive actions). */
.lc-menu-divider {
    height: 1px;
    margin: 0.25rem 0;
    background-color: var(--border);
}

/* LC-376: per-message hover action toolbar. Compact square icon buttons in the
   elevated chip; the long tail of actions collapses into a `...` overflow menu
   (.lc-msg-menu) built from a native <details> + .lc-menu-item rows. */
.lc-msg-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 1.625rem;
    width: 1.75rem;
    border-radius: 0.375rem;
    color: var(--content-muted);
    cursor: pointer;
}
.lc-msg-action:hover {
    background-color: var(--surface-sunken);
    color: var(--content);
}
.lc-msg-action svg {
    height: 1rem;
    width: 1rem;
}
.lc-msg-menu {
    min-width: 11rem;
    padding: 0.25rem;
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    background-color: var(--surface-elevated);
    box-shadow: 0 8px 24px rgb(0 0 0 / 0.12);
    /* LC-598: the menu ran most of the viewport at 15 rows. Cap it against the
       space the delegated handler in room/messages.html measured (--lc-menu-max,
       set on open); internal scroll is the last resort after the upward flip
       below, not the first response. The static fallback matters for the
       no-JS/first-paint case. */
    max-height: var(--lc-menu-max, 26rem);
    overflow-y: auto;
    overscroll-behavior: contain;
}
/* LC-598: flip upward when the row is near the viewport bottom. The handler adds
   this class instead of the menu always growing downward off-screen. */
.lc-msg-menu.lc-menu-up {
    top: auto;
    bottom: 100%;
    margin-top: 0;
    margin-bottom: 0.25rem;
}
/* LC-598 follow-up: when the menu is height-capped and has to scroll (a short or
   mid-height viewport where even the upward flip cannot fit the full list), the
   scroll starts at the top, so the destructive Delete - the last row - is the
   one that lands below the fold and reads as unreachable. Pin the danger group
   to the bottom edge of the scroll viewport so Delete is always visible while the
   middle of the list scrolls under it. A no-op when the menu fits without
   scrolling (the group is already at the bottom). The menu's own bottom padding
   is moved into the sticky group so scrolled rows cannot peek through the gap
   beneath it. Scoped by :has() so menus without a danger action (someone else's
   message with no delete right) keep their normal padding. */
.lc-msg-menu:has(.lc-menu-item-danger) {
    padding-bottom: 0;
}
.lc-msg-menu > .lc-menu-group:has(.lc-menu-item-danger) {
    position: sticky;
    bottom: 0;
    padding-bottom: 0.25rem;
    background-color: var(--surface-elevated);
}
/* LC-598: grouped sections. The separator is a border on the group rather than a
   standalone .lc-menu-divider node, so a group that renders empty (or collapses,
   like the tools cluster) takes its rule with it and cannot leave a doubled or
   leading divider behind. */
.lc-menu-group + .lc-menu-group {
    margin-top: 0.25rem;
    padding-top: 0.25rem;
    border-top: 1px solid var(--border);
}
/* LC-598: every member of the tools group is capability-gated, so the group is
   hidden by default and revealed only when something in it can actually show:
   an LLM ([data-lc-llm]) or embeddings ([data-lc-embeddings]) page root, a
   browser with speech synthesis (no .lc-no-tts on <html>), or an author/mod
   viewer who gets the Askama-gated acknowledgement item. Mirrors the existing
   per-item gates at the top of this file rather than restating their logic. */
.lc-menu-group-tools {
    display: none;
}
[data-lc-llm] .lc-menu-group-tools,
[data-lc-embeddings] .lc-menu-group-tools,
.lc-menu-group-tools:has(.lc-menu-item-ack) {
    display: block;
}
html:not(.lc-no-tts) .lc-menu-group-tools:has(.lc-read-aloud) {
    display: block;
}
/* Keep the hover chip visible while one of its <details> popovers (overflow
   menu or flag) is open, so moving the mouse off the row does not dismiss it. */
.lc-msg-actions:has(details[open]) {
    opacity: 1;
}

/* LC-377: follow-up gutter timestamp. A grouped (follow-up) message hides its
   author/time header; on row hover a compact HH:MM appears in the empty avatar
   gutter to the left of the body (which is indented `pl-8` to align under the
   author name). Absolutely placed so it never reserves layout height at rest. */
.lc-followup-ts {
    position: absolute;
    left: 0;
    width: 3.75rem;
    padding-right: 0.5rem;
    text-align: right;
    font-size: 0.625rem;
    line-height: 1.5rem;
    color: var(--content-subtle);
    opacity: 0;
    pointer-events: none;
    font-variant-numeric: tabular-nums;
}
.group:hover .lc-followup-ts {
    opacity: 1;
}

/* LC-379: day separator. A hairline rule on each side of a small rounded, muted
   pill label (Slack/Discord style). The server render (room/message.html) and
   the client insert for live day-boundary arrivals (partials/auto_scroll.html)
   share these classes verbatim, so the two markups stay in sync by construction
   instead of by matching long Tailwind strings. */
.lc-day-divider {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 0;
}
.lc-day-rule {
    flex: 1 1 0%;
    height: 1px;
    background-color: var(--border);
}
/* LC-569: the day divider is bare text between hairlines (no bordered pill,
   no uppercase), matching the comps and the prototype. */
.lc-day-chip {
    flex: none;
    padding: 0 0.5rem;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
    color: var(--content-subtle);
}
/* LC-569: the unread separator is the ACCENT "new messages" divider, not a
   danger-red pill: accent text between accent hairlines. */
.lc-day-rule-danger {
    background-color: var(--accent);
    opacity: 0.5;
}
.lc-day-chip-danger {
    border: none;
    background-color: transparent;
    padding: 0;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
    color: var(--accent);
}

/* LC-434: call/system events - a centered, muted timeline row distinct from
   user messages. Icon + actor + event text + time, all on one centered line.
   LC-680: de-emphasized further (smaller, quieter, tighter) so the human
   conversation is the loud thing and these events read as subordinate. */
.lc-sysevent {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.3125rem;
    padding: 0.0625rem 1rem;
    font-size: 0.6875rem;
    line-height: 1.2;
    color: var(--content-subtle);
    text-align: center;
}
.lc-sysevent-ico {
    display: inline-flex;
    flex: none;
    color: var(--content-subtle);
    opacity: 0.85;
}
.lc-sysevent-ico svg { height: 0.75rem; width: 0.75rem; }
.lc-sysevent-text { min-width: 0; }
.lc-sysevent-link {
    color: var(--accent);
    font-weight: 500;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.lc-sysevent-link:hover { text-decoration: none; }
.lc-sysevent-time {
    flex: none;
    color: var(--content-subtle);
    font-variant-numeric: tabular-nums;
    opacity: 0.75;
}

/* LC-680: collapsed group of consecutive call events. The <summary> is a quiet,
   centered, clickable row (event count + any transcript links); expanding
   reveals the individual .lc-sysevent rows. Native disclosure triangle is
   replaced by a rotating chevron so the affordance matches both densities. */
.lc-sysgroup { margin: 0.0625rem 0; }
.lc-sysgroup-sum {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.375rem;
    padding: 0.125rem 1rem;
    font-size: 0.6875rem;
    line-height: 1.2;
    color: var(--content-muted);
    text-align: center;
    cursor: pointer;
    list-style: none;
    user-select: none;
}
.lc-sysgroup-sum::-webkit-details-marker { display: none; }
.lc-sysgroup-sum:hover { color: var(--content); }
.lc-sysgroup-count { font-weight: 500; }
/* Transcript links stay prominent while collapsed - the useful part is one
   click away without expanding. Rendered as small accent chips. */
.lc-sysgroup-tlink {
    flex: none;
    padding: 0.0625rem 0.375rem;
    border-radius: 9999px;
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    text-decoration: none;
}
.lc-sysgroup-tlink:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
/* Rotating chevron affordance. */
.lc-sysgroup-toggle {
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg);
    opacity: 0.6;
    transition: transform 0.15s ease;
}
.lc-sysgroup[open] .lc-sysgroup-toggle { transform: rotate(-135deg); }

/* LC-380: composer controls. The two tiers share one icon-button language: a
   denser format toolbar (.lc-fmt-btn) and the larger bottom action row
   (.lc-composer-btn), both muted-to-content with a sunken hover. The primary
   Send (.lc-composer-send) is a filled-accent square that anchors the row. */
/* LC-574: single composer control bar under a full-width input. Wraps on narrow
   widths so the row never overflows and every control can reach a 44px touch
   target; the right cluster (counter, Preview, ttl, Send) keeps to the trailing
   edge via margin-left:auto and stays together as the bar wraps. */
.lc-composer-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.125rem;
    padding-top: 0.125rem;
    color: var(--content-muted);
}
.lc-composer-bar-right {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}
.lc-composer-sep {
    flex: none;
    width: 1px;
    height: 1.25rem;
    margin: 0 0.35rem;
    background: var(--border);
}
/* LC-574: unify the two composer button sizes (were 28px / 36px) so they align
   on one bar. Both classes are composer-only. */
.lc-fmt-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 2rem;
    min-width: 2rem;
    padding: 0 0.375rem;
    border-radius: 0.375rem;
    color: var(--content-muted);
    font-size: 0.8125rem;
    line-height: 1;
}
.lc-fmt-btn:hover {
    background-color: var(--surface-sunken);
    color: var(--content);
}
.lc-fmt-btn svg {
    height: 1rem;
    width: 1rem;
}
.lc-composer-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    height: 2rem;
    width: 2rem;
    border-radius: 0.5rem;
    color: var(--content-muted);
}
.lc-composer-btn:hover {
    background-color: var(--surface-sunken);
    color: var(--content);
}
.lc-composer-btn svg {
    height: 1.25rem;
    width: 1.25rem;
}
.lc-composer-send {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    height: 2rem;
    width: 2rem;
    border-radius: 0.5rem;
    background-color: var(--accent);
    color: var(--accent-content);
}
.lc-composer-send:hover {
    background-color: var(--accent-hover);
}
.lc-composer-send:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}
.lc-composer-send svg {
    height: 1.25rem;
    width: 1.25rem;
}
/* LC-565: keyboard focus ring on the composer controls, matching the app-wide
   :focus-visible idiom (.lc-set-tab / .lc-cbtn / .lc-switch). These icon
   buttons previously fell back to the UA default outline, inconsistent with
   the rest of the redesigned chrome. --ring is defined in all four themes. */
.lc-fmt-btn:focus-visible,
.lc-composer-btn:focus-visible,
.lc-composer-send:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--ring);
}

/* Touch-device tap targets (WCAG 2.5.5). Guarded on `pointer: coarse` so
   mouse / trackpad layouts keep the denser icon sizing. The redesign's icon
   controls are 28-36px; on touch they get a 44px MINIMUM.
   Height is bumped everywhere - it can never cause horizontal overflow. Width
   is only bumped where the container absorbs it: .lc-set-tab is already >=44px
   wide (full-width column, or nowrap text tabs in the mobile scroll rail), so
   min-height alone makes it a true 44px target. The composer button rows and
   the room-header action cluster are fixed-width dense single lines (up to ~10
   icon buttons + the textarea / the room title), so forcing 44px WIDTH there
   overflows a ~360px viewport; those get height only. Full 44x44 on those dense
   rows needs a mobile reflow. LC-574 did that reflow for the composer (the
   .lc-composer-bar now wraps under a full-width input), so the composer buttons
   below take a full 44x44 min-width too. The room-header action cluster
   (.lc-header-action) still does not wrap, so it stays height-only - it clears
   the 24px WCAG 2.5.8 AA floor on both axes. The call bar (.lc-cbtn) is the
   other true-44x44 case: its inner cluster scrolls horizontally on mobile. */
@media (pointer: coarse) {
    .lc-set-tab,
    .lc-header-action,
    .lc-fmt-btn,
    .lc-composer-btn,
    .lc-composer-send,
    .lc-cbtn {
        min-height: 44px;
    }
    /* LC-574: the composer control bar wraps, so its buttons can take a 44px
       min-width without overflowing - completing the dense-row 44x44 the LC-573
       decision had deferred for the composer. */
    .lc-fmt-btn,
    .lc-composer-btn,
    .lc-composer-send {
        min-width: 44px;
    }
    /* LC-573: call-control buttons (.lc-cbtn) were omitted from the height rule
       in PR #535. They also collapse to icon-only on a narrow bar (@container
       lc-callbar), so add min-width for a true 44x44; justify-content centres
       the icon in the widened box. Safe because .lc-callbar-controls scrolls
       horizontally rather than forcing page overflow. */
    .lc-cbtn {
        min-width: 44px;
        justify-content: center;
    }
}

/* LC-399: live markdown highlight overlay for the composer. The .lc-mdback
   backdrop renders the styled text and the box chrome; the .lc-mdinput textarea
   sits on top transparent (text + background) with a visible caret. The two
   MUST share identical box metrics - font, line-height, padding, border width,
   wrapping - or the highlight drifts from the caret, so every metric below is
   set on both and only colour / weight / decoration / background ever differs
   between a token and plain text (changing glyph advance would desync). */
.lc-mdwrap {
    position: relative;
    display: block;
}
.lc-mdback,
.lc-mdinput {
    box-sizing: border-box;
    width: 100%;
    margin: 0;
    /* LC-574: borderless - the composer card already frames the input. The 1px
       width is kept (transparent) so .lc-mdback and .lc-mdinput stay metric-
       identical and the markdown highlight overlay never drifts; focus shows a
       ring instead (see .lc-mdinput:focus). */
    border: 1px solid transparent;
    border-radius: 0.5rem;
    /* LC-676 #5: tighter vertical padding so the empty/single-line composer is
       compact, not a tall band. Set on BOTH .lc-mdback and .lc-mdinput (this
       shared rule) so the LC-399 overlay metrics stay identical and the
       highlight never drifts from the caret. */
    padding: 0.375rem 0.75rem;
    font-family: inherit;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    letter-spacing: normal;
    white-space: pre-wrap;
    overflow-wrap: break-word;
    word-break: break-word;
    max-height: 10rem;
}
.lc-mdback {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
    background: var(--surface-elevated);
    color: var(--content);
    border-color: transparent;
}
.lc-mdinput {
    position: relative;
    z-index: 1;
    display: block;
    resize: none;
    overflow-y: auto;
    background: transparent;
    color: transparent;
    caret-color: var(--content);
}
.lc-mdinput:focus {
    outline: none;
    /* LC-574: borderless input - a 2px ring shows focus (matches the LC-565
       focus-ring idiom) without a resting border. */
    box-shadow: 0 0 0 2px var(--ring);
}
.lc-mdinput::placeholder {
    color: var(--content-muted);
    opacity: 1;
}
/* LC-553: the thread reply box reuses .lc-mdinput for its chrome (borderless,
   focus ring, shared metrics) but has NO .lc-mdback highlight backdrop behind
   it - that overlay only exists in the main composer's .lc-mdwrap. Without the
   backdrop the transparent .lc-mdinput text renders invisible (only the caret
   shows), so give the solo thread textarea a solid text colour and a field
   background. */
#thread-panel .lc-mdinput {
    color: var(--content);
    background: var(--surface-elevated);
}
/* When the LC-276 preview pane is shown the textarea gets `hidden`; collapse the
   whole overlay so the preview pane (its flex sibling) takes the slot. */
.lc-mdwrap:has(> .lc-mdinput.hidden) {
    display: none;
}
/* Token styling. Colour / weight / decoration / background only - never
   font-size / font-family / letter-spacing, which change glyph advance and
   drift the backdrop off the caret. That is why the markers are made to recede
   by OPACITY, not by shrinking them: a smaller marker would desync the caret. */
/* LC-399 follow-up: the markers were too loud at 0.55 (half-visible), so
   `**Hehe**` still read as literal asterisks rather than a bold "Hehe". Drop
   them to 0.3 so they fade to faint ghosts and the emphasized text dominates,
   and lift bold to 700 so the weight contrast is unmistakable. */
.lc-mdback .lc-mdm { color: var(--content-muted); opacity: 0.3; }
.lc-mdback strong { font-weight: 700; color: var(--content); }
.lc-mdback em { font-style: italic; }
.lc-mdback del { text-decoration: line-through; color: var(--content-muted); }
.lc-mdback .lc-mdcode {
    background: var(--surface-sunken);
    border-radius: 0.25rem;
    color: var(--content);
}
.lc-mdback .lc-mdh { font-weight: 700; color: var(--content); }
.lc-mdback .lc-mdq { color: var(--content-muted); font-style: italic; }
.lc-mdback .lc-mdlink { color: var(--accent); }
.lc-mdback .lc-mdurl { color: var(--content-muted); text-decoration: underline; }

/* LC-371 / LC-381: active sidebar room / DM row. LC-381 lightens the prior
   full soft-accent fill (which read heavy in-room) to a neutral raised surface
   plus an accent left bar and accent-colored, bold label - a clear but quiet
   active state. The inset shadow bar follows the rounded corners and shifts no
   layout. Paired in the row templates with a bold-unread / muted-room treatment
   driven by per-row conditionals. */
/* LC-569: the active room row is a SOLID accent fill with accent-content text
   (this is what the comps and the prototype show), not a raised tint with accent
   text plus a left bar. The unread pill inverts on it so it stays legible. */
.lc-room-row-active {
    background-color: var(--accent);
    color: var(--accent-content);
    font-weight: 600;
}
.lc-room-row-active .lc-count-pill {
    background-color: var(--accent-content);
    color: var(--accent);
}
/* LC-576: descendants of the active row set their own color with a Tailwind
   utility (the leading # glyph and the draft pencil use text-content-subtle,
   the DM custom-status line uses text-content-muted). A class beats an
   inherited value, so they kept their resting grey against the solid accent
   fill instead of inverting with the row. Restate them in the row's own
   content color and carry the hierarchy with opacity. The mention badge is
   exempt on purpose: it is a self-contained danger pill with its own fill. */
.lc-room-row-active .text-content-subtle,
.lc-room-row-active .text-content-muted {
    color: var(--accent-content);
    opacity: 0.75;
}

/* LC-569: unread count pill. Neutral at rest (inside .lc-sidebar the surface
   tokens resolve to the sidebar ramp), accent only via the active row above. */
.lc-count-pill {
    margin-left: auto;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 0.375rem;
    border-radius: 999px;
    background-color: var(--surface-elevated);
    color: var(--content);
    font-size: 0.6875rem;
    font-weight: 600;
    display: inline-grid;
    place-items: center;
}

/* LC-612: sidebar in-call indicator - a live green dot + participant count on a
   room row whose huddle is active. Uses the same pill metrics as the count pill
   but a success accent so it reads as "call in progress", not "unread". */
.lc-incall-pill {
    margin-left: 0.25rem;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    height: 1.25rem;
    padding: 0 0.375rem;
    border-radius: 999px;
    background-color: color-mix(in srgb, var(--success) 18%, transparent);
    color: var(--success);
    font-size: 0.6875rem;
    font-weight: 600;
}
.lc-incall-dot {
    width: 0.4375rem;
    height: 0.4375rem;
    border-radius: 999px;
    background-color: var(--success);
}
@media (prefers-reduced-motion: no-preference) {
    .lc-incall-dot { animation: lc-incall-pulse 2s ease-in-out infinite; }
}
@keyframes lc-incall-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

/* LC-371: device-local collapsible sidebar groups (Starred / Channels / DMs).
   Body hiding + chevron rotation are keyed on the <html data-lc-collapsed-groups>
   attribute (set no-flash by base.html), so a collapsed group never flashes open
   and the state survives sidebar OOB swaps with no JS scan. */
html[data-lc-collapsed-groups~="starred"] [data-lc-sb-group="starred"] [data-lc-sb-group-body],
html[data-lc-collapsed-groups~="rooms"] [data-lc-sb-group="rooms"] [data-lc-sb-group-body],
html[data-lc-collapsed-groups~="dms"] [data-lc-sb-group="dms"] [data-lc-sb-group-body] {
    display: none;
}
[data-lc-group-chevron] {
    transition: transform 0.15s ease;
}
html[data-lc-collapsed-groups~="starred"] [data-lc-sb-group="starred"] [data-lc-group-chevron],
html[data-lc-collapsed-groups~="rooms"] [data-lc-sb-group="rooms"] [data-lc-group-chevron],
html[data-lc-collapsed-groups~="dms"] [data-lc-sb-group="dms"] [data-lc-group-chevron] {
    transform: rotate(-90deg);
}

/* LC-373: shared main-pane header skeleton. One height / padding / border across
   the room, DM, and home headers, plus one action-button treatment (icon, or
   icon+label) so the top bar reads consistently everywhere. The room header also
   carries data-lc-density-tight="header", whose compact rule still wins on
   padding (higher specificity) so density behaves as before. */
.lc-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    /* LC-576: 14px/20px to match the prototype top bar. The 20px side padding
       also puts the room title on the SAME gutter as the timeline (#messages is
       px-5), which the old 16px did not - the title hung 4px left of every
       message row below it. */
    min-height: 3.5rem;
    padding: 0.875rem 1.25rem;
    border-bottom: 1px solid var(--border);
}
.lc-header-actions {
    display: flex;
    align-items: center;
    /* LC-576: the prototype spaces bare header glyphs 14px apart. These actions
       each carry 8px of internal padding, so 6px between the boxes lands at the
       same ~14px optical gap between glyphs without the hover tints colliding. */
    gap: 0.375rem;
    flex: none;
}
/* Header action: icon-only (square) or icon+label (auto width). Consistent
   muted-to-content hover with a sunken tint, replacing the mix of bordered
   pills and bare icon buttons the headers used before. */
.lc-header-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    height: 2rem;
    min-width: 2rem;
    padding: 0 0.5rem;
    border-radius: 0.375rem;
    font-size: 0.75rem;
    color: var(--content-muted);
    cursor: pointer;
}
.lc-header-action:hover {
    background-color: var(--surface-sunken);
    color: var(--content);
}
.lc-header-action-danger:hover {
    color: var(--danger);
}
.lc-header-action svg { height: 1.25rem; width: 1.25rem; }
/* LC-576: the prototype's header actions are icon-only (.hdr-ico) - the labels
   live in the tooltip / aria-label, which every one of these already carries.
   The text labels made the bar wide enough to push the notify pill and overflow
   off the edge on a normal window. The notify pill keeps its label on purpose:
   it shows STATE ("All" / "Muted"), not just an action name. */
.lc-header-actions .lc-header-action > span { display: none; }
.lc-header-actions [id^="lc-notify-toggle-"] > span { display: inline; }

/* LC-576: header search - an icon that discloses the existing combobox, rather
   than a persistent field taking a fifth of the bar (prototype has an icon). */
.lc-header-search { position: relative; }
.lc-header-search > summary {
    list-style: none;
    cursor: pointer;
}
.lc-header-search > summary::-webkit-details-marker { display: none; }
.lc-header-search[open] > summary {
    background-color: var(--surface-sunken);
    color: var(--content);
}
.lc-header-search-pop {
    position: absolute;
    right: 0;
    top: calc(100% + 0.375rem);
    z-index: 40;
    width: 17rem;
    padding: 0.5rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    background-color: var(--surface-elevated);
    box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.2);
}
.lc-header-search-pop input[name="q"] { width: 100%; }

/* LC-576: favorite star beside the room title (prototype .hdr-title .star). */
.lc-header-star {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 1.5rem;
    width: 1.5rem;
    border-radius: 0.375rem;
    font-size: 1.125rem;
    line-height: 1;
    color: var(--content-subtle);
    cursor: pointer;
}
.lc-header-star:hover { color: #f59e0b; background-color: var(--surface-sunken); }
.lc-header-star-on { color: #f59e0b; }
/* LC-436: Mute is an icon toggle - an sr-only checkbox (keeps the hx-post mute
   wiring) swaps a bell for a slashed bell and tints the action when muted. */
.lc-header-action .lc-mute-off { display: none; }
.lc-header-action:has(input:checked) .lc-mute-on { display: none; }
.lc-header-action:has(input:checked) .lc-mute-off { display: inline-flex; }
.lc-header-action:has(input:checked) { color: var(--danger); }

/* LC-384: emoji-picker grid cell. Consistent square cells with a sunken hover,
   for both the reaction picker (reactions::get_picker) and parity with the
   composer picker. */
.lc-emoji-cell {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.875rem;
    height: 1.875rem;
    border-radius: 0.375rem;
    font-size: 1.125rem;
    line-height: 1;
    cursor: pointer;
}
.lc-emoji-cell:hover {
    background-color: var(--surface-sunken);
}
/* LC-391: the filter hides non-matching cells (and the tab strip) via the
   [hidden] attribute, but the author display rules above (.lc-emoji-cell ->
   inline-flex, .lc-emoji-tabs -> flex) override the UA [hidden]{display:none}
   regardless of specificity, so without these the filter looked dead. The
   attribute-qualified selector (0,2,0) beats the bare class (0,1,0); no
   !important needed. Sections already hide (default block display). */
.lc-emoji-cell[hidden],
.lc-emoji-tabs[hidden] {
    display: none;
}

/* LC-509: shared styling for the composer's autocomplete popovers (slash
   commands, @mentions, :emoji:). The keyboard-highlighted option must be
   visually distinct, not just aria-selected; mouse hover matches it so the two
   selection modes look identical. */
.lc-ac-header {
    padding: 0.25rem 0.75rem;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--content-subtle);
    border-bottom: 1px solid var(--border);
}
.lc-ac-option:hover,
.lc-ac-option[aria-selected="true"] {
    background-color: var(--surface-sunken);
}

/* ============================================================================
   LC-402: in-call (voice/video) UI design system. Theme-token driven so the
   call surface follows light / dark / high-contrast like the rest of the app.
   JS (voice.js / call.js / transcribe.js) targets data-lc-* hooks and element
   ids, never these classes, so styling here is free to change.
   ============================================================================ */

/* ---- Control bar -------------------------------------------------------- */
.lc-callbar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    border-bottom: 1px solid var(--border);
    background: var(--surface-elevated);
  color: var(--content);
    color: var(--content);
    padding: 0.5rem 1rem;
    /* LC-416: a query container so the control buttons can drop their labels to
       icon-only when the bar itself is narrow - notably while the transcript
       drawer reserves width - instead of overflowing / overlapping. */
    container: lc-callbar / inline-size;
}
/* LC-412: bottom tray variant for the 1:1 call modal - the bar sits under the
   video, so the divider is on top and the clusters centre. */
.lc-callbar--bottom {
    flex: none;
    justify-content: center;
    border-bottom: none;
    border-top: 1px solid var(--border);
    padding: 0.625rem 1rem;
}
.lc-callbar-group {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
}
/* LC-416: isolate the destructive Leave / Hang up cluster with extra space so
   it never sits flush against the transcription controls. */
.lc-callbar-group--end { margin-left: 0.375rem; }
/* LC-409: wrapper around both control clusters - one flex row on desktop, a
   horizontally-scrollable row under the title on mobile (see the media query). */
.lc-callbar-controls {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.lc-callbar-sep {
    align-self: stretch;
    width: 1px;
    margin: 0.125rem 0.25rem;
    background: var(--border);
}
/* base call control button */
.lc-cbtn {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--content);
    padding: 0.375rem 0.75rem;
    font-size: 0.8125rem;
    line-height: 1.25rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.lc-cbtn:hover { background: var(--surface-sunken); }
.lc-cbtn:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); }
.lc-cbtn:disabled { opacity: 0.5; cursor: not-allowed; }
.lc-cbtn svg { height: 1rem; width: 1rem; }
/* main.css loads after tailwind-built.css, so without this the .lc-cbtn display
   would win over the .hidden utility the call JS toggles (cf. LC-391). */
.lc-cbtn.hidden { display: none; }

/* ---- LC-416: icon + collapsible label control buttons ------------------- */
.lc-cbtn-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    line-height: 0;
}
.lc-cbtn-label { white-space: nowrap; }
/* Mute swaps mic <-> slashed-mic by aria-pressed; other state is colour only. */
.lc-cbtn-ico--on { display: none; }
.lc-cbtn[aria-pressed="true"] .lc-cbtn-ico--off { display: none; }
.lc-cbtn[aria-pressed="true"] .lc-cbtn-ico--on { display: inline-flex; }
/* Always icon-only (the device gear): keep it square. */
.lc-cbtn--icon { padding-left: 0.5rem; padding-right: 0.5rem; }
/* When the bar is narrow, collapse every labelled control to its icon so the
   row compresses instead of overflowing. The device gear is already icon-only;
   leave the Join button labelled (it shows alone, pre-join, with room). */
@container lc-callbar (max-width: 860px) {
    .lc-callbar-controls .lc-cbtn:not(.lc-cbtn--join) .lc-cbtn-label { display: none; }
    .lc-callbar-controls .lc-cbtn:not(.lc-cbtn--join) { padding-left: 0.5rem; padding-right: 0.5rem; }
}
.lc-voice-countchip { display: inline-flex; align-items: center; gap: 0.25rem; }
.lc-voice-countchip.hidden { display: none; }
/* generic toggle on-state (Mute active, video on, screen sharing) */
.lc-cbtn[aria-pressed="true"] {
    background: var(--accent-surface);
    border-color: var(--accent);
    color: var(--accent-surface-content);
}
/* primary: Join */
.lc-cbtn--join {
    background: var(--success);
    border-color: var(--success);
    color: var(--success-content);
}
.lc-cbtn--join:hover { background: var(--success); filter: brightness(0.94); }
/* destructive: Leave / Hang up */
.lc-cbtn--leave {
    background: var(--danger);
    border-color: var(--danger);
    color: var(--danger-content);
}
.lc-cbtn--leave:hover { background: var(--danger); filter: brightness(0.94); }
/* Transcribe: idle is the quiet neutral .lc-cbtn; when recording it is an
   affirmative, saturated "on" - a solid red fill (the same solid --danger /
   --danger-content pairing as Leave, so it reads as live, not disabled, in
   every theme) with the record glyph's core pulsing. LC-416 follow-up: the old
   pale --danger-surface fill read as a hover-ghost / disabled control, putting
   the strongest emphasis on the inactive buttons instead of the live one. */
.lc-cbtn--rec[aria-pressed="true"] {
    background: var(--danger);
    border-color: var(--danger);
    color: var(--danger-content);
}
.lc-cbtn--rec[aria-pressed="true"]:hover { background: var(--danger); filter: brightness(0.94); }
/* LC-416: Transcribe uses a record glyph (ring + core). Off-state inherits the
   button text colour; the active (recording) state recolours via lc-cbtn--rec
   above and the inner core pulses, so the toggle reads as a live record button
   and pairs with the drawer header dot. */
.lc-rec-glyph svg { height: 1.05rem; width: 1.05rem; }
.lc-cbtn--rec[aria-pressed="true"] .lc-rec-core {
    animation: lc-rec-pulse 1.6s ease-in-out infinite;
}

/* ---- Participant grid --------------------------------------------------- */
/* LC-416 follow-up: cap each cell to a bounded max (not 1fr) and centre the
   row. With a 1fr max the 2-up case ballooned to two huge edge-to-edge tiles
   (the right one hard against the viewport), which read as unbalanced. A
   bounded max keeps tiles a sensible size and `justify-content: center` keeps
   even gutters on both sides for 1 / 2 / 3+; tracks still shrink toward the min
   when many participants are present, so cells stay equal-width and nothing
   overflows. */
.lc-voice-grid {
    display: grid;
    gap: 1rem;
    align-content: start;
    justify-content: center;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 480px));
}
/* LC-408 fix: the grid's shown/hidden state is a class, NOT an inline
   `display`. An inline `display:grid` would beat the stylesheet and stop the
   screen-share stage rule below from switching the container to flex. */
.lc-voice-grid.lc-voice-grid--hidden { display: none; }
/* Alone in the call (the waiting placeholder is present): keep the lone self
   tile a sensible size instead of ballooning to the full row width. */
.lc-voice-grid:has(.lc-voice-waiting) {
    grid-template-columns: minmax(0, 480px);
    justify-content: center;
}
.lc-voice-tile {
    position: relative;
    overflow: hidden;
    border-radius: 0.75rem;
    background: var(--surface-sunken);
    border: 2px solid transparent;
    aspect-ratio: 16 / 9;
    min-height: 140px;
}
/* active-speaker highlight (voice.js toggles data-speaking via an analyser) */
.lc-voice-tile[data-speaking="true"] {
    border-color: var(--success);
    box-shadow: 0 0 0 3px var(--success-surface);
}
/* LC-416: animated equalizer in the name pill, the same active-speaker cue on
   every tile (the lone bars from the reference, made consistent). Shown only
   while the tile is speaking; the three bars bob out of phase. */
.lc-voice-speaking {
    display: none;
    align-items: flex-end;
    gap: 2px;
    height: 0.8125rem;
    flex: none;
}
.lc-voice-tile[data-speaking="true"] .lc-voice-speaking { display: inline-flex; }
.lc-voice-speaking i {
    display: block;
    width: 3px;
    height: 40%;
    border-radius: 1px;
    background: var(--success);
    animation: lc-eq 0.9s ease-in-out infinite;
}
.lc-voice-speaking i:nth-child(2) { animation-delay: 0.15s; }
.lc-voice-speaking i:nth-child(3) { animation-delay: 0.3s; }
@keyframes lc-eq { 0%, 100% { height: 30%; } 50% { height: 100%; } }
.lc-voice-tile video {
    position: absolute;
    inset: 0;
    height: 100%;
    width: 100%;
    object-fit: cover;
}
.lc-voice-avatar {
    position: absolute;
    inset: 0;
    margin: auto;
    height: 5rem;
    width: 5rem;
    border-radius: 9999px;
    object-fit: cover;
}
.lc-voice-name {
    position: absolute;
    bottom: 0.5rem;
    left: 0.5rem;
    z-index: 10;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    max-width: calc(100% - 1rem);
    border-radius: 0.375rem;
    background: color-mix(in srgb, var(--surface) 78%, transparent);
    padding: 0.125rem 0.5rem;
    font-size: 0.8125rem;
    color: var(--content);
}
.lc-voice-name-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.lc-voice-you {
    flex: none;
    border-radius: 0.25rem;
    background: var(--accent);
    color: var(--accent-content);
    padding: 0 0.25rem;
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}
.lc-voice-mute-badge {
    display: none;
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 10;
    height: 1.5rem;
    width: 1.5rem;
    align-items: center;
    justify-content: center;
    border-radius: 9999px;
    background: var(--danger);
    color: var(--danger-content);
}
.lc-voice-mute-badge svg { height: 0.875rem; width: 0.875rem; }
.lc-voice-tile[data-muted="true"] .lc-voice-mute-badge { display: inline-flex; }
/* LC-410: per-peer connection-quality glyph, inside the name pill. Hidden
   until the tile gets a data-quality (never on the self tile). Recoloured per
   state; "reconnecting" pulses. */
.lc-voice-quality { display: none; line-height: 0; flex: none; }
.lc-voice-tile[data-quality] .lc-voice-quality { display: inline-flex; }
.lc-voice-quality svg { height: 0.875rem; width: 0.875rem; }
.lc-voice-tile[data-quality="good"] .lc-voice-quality { color: var(--success); }
.lc-voice-tile[data-quality="degraded"] .lc-voice-quality { color: var(--warning); }
.lc-voice-tile[data-quality="reconnecting"] .lc-voice-quality {
    color: var(--warning);
    animation: lc-rec-pulse 1.4s ease-in-out infinite;
}
.lc-voice-tile[data-quality="failed"] .lc-voice-quality { color: var(--danger); }
/* "Waiting for others" empty state when joined but alone */
.lc-voice-waiting {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 6rem;
    color: var(--content-muted);
    font-size: 0.875rem;
}

/* ---- LC-700: huddle dock (ad-hoc group huddles; scoped to .lc-huddle) ------
   A huddle is a lightweight stay-in-chat mode, not a co-equal 50/50 split. It
   docks at the room foot: audio-only participants render as compact chips in a
   wrapping row so the timeline stays primary, and the dock only grows to full
   16/9 video tiles when a camera or screen-share is live. A collapse toggle
   (live.js) shrinks it to just the control bar. These rules never touch the
   full-screen voice-channel view, which has no .lc-huddle root. */

/* The bar is a query container so the shared .lc-callbar-controls rule collapses
   labelled buttons to icons when the dock is narrow (reused from the call bar). */
.lc-huddle-bar { container: lc-callbar / inline-size; }
.lc-huddle-collapse .lc-cbtn-ico { transform: rotate(180deg); }

/* Collapsed: keep the control bar, drop the participant area. Instant (a
   deterministic display toggle rather than a max-height/specificity fight); the
   chevron still rotates for a visible affordance. */
.lc-huddle--collapsed [data-lc-voice-grid],
.lc-huddle--collapsed [data-lc-voice-preview] { display: none; }
.lc-huddle--collapsed .lc-huddle-collapse .lc-cbtn-ico { transform: rotate(0deg); }
@media (prefers-reduced-motion: no-preference) {
    .lc-huddle-collapse .lc-cbtn-ico { transition: transform 0.2s ease; }
    .lc-huddle [data-lc-voice-grid] { transition: max-height 0.2s ease; }
}

/* Audio-only (no tile carries a live camera or a screen-share pin): lay the
   in-call grid out as a wrapping row of compact chips. Reuses the live tile DOM
   (speaking ring, mute badge, YOU pill) - only the geometry changes. */
.lc-huddle [data-lc-voice-grid]:not(.lc-voice-grid--hidden):not(:has([data-media="video"], [data-screen])) {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    max-height: 5rem;
}
.lc-huddle [data-lc-voice-grid]:not(:has([data-media="video"], [data-screen])) .lc-voice-tile {
    aspect-ratio: auto;
    min-height: 0;
    width: auto;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.625rem 0.25rem 0.3rem;
    border-radius: 9999px;
    background: var(--surface-elevated);
}
/* Re-flow the absolutely-positioned tile internals into an inline chip. */
.lc-huddle [data-lc-voice-grid]:not(:has([data-media="video"], [data-screen])) .lc-voice-tile video,
.lc-huddle [data-lc-voice-grid]:not(:has([data-media="video"], [data-screen])) .lc-voice-tile .lc-voice-screen-badge {
    display: none;
}
.lc-huddle [data-lc-voice-grid]:not(:has([data-media="video"], [data-screen])) .lc-voice-avatar {
    position: static;
    inset: auto;
    margin: 0;
    height: 1.5rem;
    width: 1.5rem;
    display: block;
}
.lc-huddle [data-lc-voice-grid]:not(:has([data-media="video"], [data-screen])) .lc-voice-name {
    position: static;
    inset: auto;
    background: transparent;
    padding: 0;
    max-width: none;
    font-size: 0.75rem;
}
.lc-huddle [data-lc-voice-grid]:not(:has([data-media="video"], [data-screen])) .lc-voice-mute-badge {
    position: static;
    inset: auto;
    height: 1rem;
    width: 1rem;
}
/* Alone-in-huddle placeholder stays inline and quiet in the compact dock. */
.lc-huddle [data-lc-voice-grid]:not(:has([data-media="video"], [data-screen])) .lc-voice-waiting {
    grid-column: auto;
    min-height: 0;
    font-size: 0.75rem;
}

/* ---- Pre-join lobby (LC-416 follow-up) ---------------------------------- */
/* Shown before joining: previews who is already here as avatar chips, with a
   heading + primary Join CTA, or an empty state. voice.js hides the whole
   region (inline display:none) once joined, so the in-call grid takes over. */
.lc-voice-lobby {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.25rem;
    padding: 2rem 1.5rem;
    text-align: center;
}
.lc-voice-lobby-empty,
.lc-voice-lobby-active {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.875rem;
}
/* main.css loads after tailwind-built.css, so the flex above would beat the
   .hidden utility voice.js toggles; restore it explicitly (cf. LC-391). */
.lc-voice-lobby-empty.hidden,
.lc-voice-lobby-active.hidden { display: none; }
.lc-voice-lobby-empty-ico {
    display: inline-flex;
    height: 3rem;
    width: 3rem;
    color: var(--content-subtle);
}
.lc-voice-lobby-empty-ico svg { height: 3rem; width: 3rem; }
.lc-voice-lobby-title { font-size: 1.125rem; font-weight: 600; color: var(--content); }
.lc-voice-lobby-sub { font-size: 0.875rem; color: var(--content-muted); }
.lc-voice-lobby-avatars {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 1rem;
    max-width: 40rem;
}
.lc-voice-lobby-chip {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.375rem;
    width: 5rem;
}
.lc-voice-lobby-avatar {
    height: 3.5rem;
    width: 3.5rem;
    border-radius: 9999px;
    object-fit: cover;
    border: 2px solid var(--border);
    background: var(--surface-sunken);
}
.lc-voice-lobby-name {
    max-width: 5rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.75rem;
    color: var(--content-muted);
}
.lc-voice-lobby-join {
    font-size: 0.9375rem;
    padding: 0.5rem 1.25rem;
}

/* ---- Screen-share stage (LC-408) --------------------------------------- */
/* When any tile is a screen share, reshape the grid into a flexbox "stage":
   the sharing tile spans the full width (forcing a wrap) and the rest flow
   below as a centered filmstrip of small tiles. Placed after the grid rules so
   it wins; the :has() toggles automatically when voice.js sets data-screen. */
.lc-voice-grid:has(.lc-voice-tile[data-screen]) {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-content: flex-start;
    justify-content: center;
}
.lc-voice-grid:has(.lc-voice-tile[data-screen]) .lc-voice-tile[data-screen] {
    flex: 1 1 100%;
    aspect-ratio: 16 / 9;
    max-height: 70vh;
    background: #0b0c0f; /* shared screens read best on a neutral-dark stage */
}
/* Don't crop the shared screen - letterbox it instead. */
.lc-voice-grid:has(.lc-voice-tile[data-screen]) .lc-voice-tile[data-screen] video {
    object-fit: contain;
}
.lc-voice-grid:has(.lc-voice-tile[data-screen]) .lc-voice-tile:not([data-screen]) {
    flex: 0 0 clamp(120px, 16vw, 196px);
    aspect-ratio: 16 / 9;
    min-height: 0;
}
.lc-voice-grid:has(.lc-voice-tile[data-screen]) .lc-voice-waiting { display: none; }
/* "Presenting" badge, shown on a tile that is the active screen share. */
.lc-voice-screen-badge {
    display: none;
    position: absolute;
    top: 0.5rem;
    left: 0.5rem;
    z-index: 10;
    align-items: center;
    gap: 0.25rem;
    border-radius: 0.375rem;
    background: var(--accent);
    color: var(--accent-content);
    padding: 0.125rem 0.4rem;
    font-size: 0.6875rem;
    font-weight: 600;
}
.lc-voice-screen-badge svg { height: 0.8125rem; width: 0.8125rem; }
.lc-voice-tile[data-screen] .lc-voice-screen-badge { display: inline-flex; }

/* ---- Transcript drawer -------------------------------------------------- */
/* Fixed right-hand drawer, used by BOTH the voice page and the 1:1 call modal.
   On the voice page #lc-voice-root reserves space for it (below) so tiles are
   never covered; over the fullscreen 1:1 modal it simply overlays the right. */
.lc-transcript {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 62;
    display: flex;
    flex-direction: column;
    width: 20rem;
    max-width: 85vw;
    border-left: 1px solid var(--border);
    background: var(--surface-elevated);
  color: var(--content);
    color: var(--content);
    box-shadow: -8px 0 24px -12px rgba(0, 0, 0, 0.35);
    /* LC-416: slide in from the right rather than snapping, so opening the panel
       reads as reserving space (paired with the #lc-voice-root padding
       transition below) instead of squeezing the header. */
    transform: translateX(0);
    transition: transform 0.2s ease, visibility 0.2s ease;
}
/* Off-canvas + non-interactive when closed (was display:none, which could not
   animate). visibility transitions discretely so the slide-out still shows. */
.lc-transcript.hidden { transform: translateX(100%); visibility: hidden; }
#lc-voice-root { transition: padding-right 0.2s ease; }
body.lc-transcript-open #lc-voice-root { padding-right: 20rem; }

/* ---- LC-416: transcript empty state ------------------------------------- */
/* Shown until the first caption line lands (#lc-caption-log is the OOB target;
   it is empty before any segment). :has keeps it pure-CSS - no JS toggle. */
.lc-transcript-empty {
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    text-align: center;
    padding: 2.5rem 1.25rem;
    color: var(--content-muted);
}
.lc-transcript-body:has(#lc-caption-log:empty) .lc-transcript-empty { display: flex; }
.lc-transcript-empty-ico { height: 2rem; width: 2rem; color: var(--content-subtle); margin-bottom: 0.25rem; }
.lc-transcript-empty-title { font-size: 0.875rem; font-weight: 600; color: var(--content); }
.lc-transcript-empty-sub { font-size: 0.8125rem; color: var(--content-muted); max-width: 16rem; }
.lc-transcript-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex: none;
    border-bottom: 1px solid var(--border);
    padding: 0.625rem 0.75rem;
}
.lc-transcript-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 600;
    font-size: 0.875rem;
}
.lc-transcript-rec {
    height: 0.5rem;
    width: 0.5rem;
    border-radius: 9999px;
    background: var(--danger);
    animation: lc-rec-pulse 1.6s ease-in-out infinite;
}
.lc-transcript-close {
    margin-left: auto;
    display: inline-flex;
    height: 1.75rem;
    width: 1.75rem;
    align-items: center;
    justify-content: center;
    border-radius: 0.375rem;
    color: var(--content-muted);
    cursor: pointer;
}
.lc-transcript-close:hover { background: var(--surface-sunken); color: var(--content); }
.lc-transcript-body {
    position: relative;
    flex: 1;
    overflow-y: auto;
    padding: 0.5rem 0.75rem;
}
#lc-caption-log { display: flex; flex-direction: column; gap: 0.375rem; }
/* "Jump to live" affordance, revealed when the user scrolls up off the bottom. */
.lc-transcript-jump {
    position: sticky;
    bottom: 0.5rem;
    /* LC-633: centre the pill. .lc-transcript-body is a block (not a flex
       container), so `align-self` did nothing and the sticky pill sat
       left-aligned; a fit-content width with auto side margins centres it
       regardless of the parent's display mode. */
    display: none;
    width: fit-content;
    margin: 0.25rem auto 0;
    align-items: center;
    gap: 0.375rem;
    border-radius: 9999px;
    border: 1px solid var(--accent);
    background: var(--accent);
    color: var(--accent-content);
    padding: 0.25rem 0.75rem;
    font-size: 0.75rem;
    font-weight: 600;
    cursor: pointer;
    box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.4);
}
body.lc-transcript-scrolled .lc-transcript-jump { display: flex; }

/* ---- Transcript actions footer (LC-411) -------------------------------- */
/* Open / copy / download the saved transcript from inside the call. Shown only
   while a session is active (transcribe.js toggles the [hidden] attribute). */
.lc-transcript-foot {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    flex: none;
    border-top: 1px solid var(--border);
    padding: 0.5rem 0.75rem;
}
/* main.css loads after tailwind-built.css, so the display above would beat the
   UA [hidden]; restore it explicitly. */
.lc-transcript-foot[hidden] { display: none; }
.lc-transcript-action {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    border-radius: 0.375rem;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--content);
    padding: 0.1875rem 0.5rem;
    font-size: 0.75rem;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
}
.lc-transcript-action:hover { background: var(--surface-sunken); }
.lc-transcript-action:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); }

/* ---- Transcript lines (grouped by speaker, speaker + time + text) ------- */
/* LC-402: re-homed from the old dark bottom scrim into the themed drawer, so
   colours come from tokens. Consecutive lines from one speaker are grouped:
   transcribe.js adds .lc-cap-cont to repeats, hiding the speaker header. */
.lc-cap-line {
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--content);
}
/* LC-633: a consistent three-part rhythm - a clear gap before each new speaker
   block (head margin-top), a smaller gap between the name row and its first
   message (head margin-bottom), and the flat #lc-caption-log gap between every
   message line. The old negative margin on continuation lines made the
   line-to-line spacing inconsistent, so it is gone. */
.lc-cap-head {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin-top: 0.75rem;
    margin-bottom: 0.1875rem;
}
#lc-caption-log > .lc-cap-line:first-child .lc-cap-head { margin-top: 0; }
.lc-cap-line.lc-cap-cont .lc-cap-head { display: none; }
.lc-cap-speaker {
    font-weight: 600;
    font-size: 0.8125rem;
    color: var(--content);
}
.lc-cap-time {
    font-size: 0.6875rem;
    color: var(--content-subtle);
    font-variant-numeric: tabular-nums;
}
.lc-cap-text {
    display: block;
    color: var(--content-muted);
}

@keyframes lc-rec-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; }
}
@media (prefers-reduced-motion: reduce) {
    .lc-rec-core, .lc-transcript-rec, .lc-voice-speaking i,
    .lc-voice-tile[data-quality="reconnecting"] .lc-voice-quality { animation: none; }
    .lc-transcript, #lc-voice-root { transition: none; }
}

/* ---- LC-409: mobile / responsive call pass ----------------------------- */
@media (max-width: 640px) {
    /* Control bar: title on its own row, the two control clusters in one
       horizontally-scrollable row beneath it - instead of wrapping into
       several cramped lines. */
    .lc-callbar {
        flex-direction: column;
        align-items: stretch;
        flex-wrap: nowrap;
        gap: 0.5rem;
        padding: 0.5rem 0.75rem;
    }
    /* LC-412: the 1:1 modal bar has no title row to stack, so keep it a centred
       wrapping row of controls rather than a column. */
    .lc-callbar--bottom {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
    }
    .lc-callbar-spacer { display: none; }
    .lc-callbar-controls {
        min-width: 0;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: thin;
        padding-bottom: 0.125rem;
    }
    /* Keep each button at full label width and scroll the row, don't squash. */
    .lc-callbar-controls .lc-cbtn,
    .lc-callbar-controls .lc-callbar-group,
    .lc-callbar-controls .lc-callbar-sep { flex: none; }
    /* Comfortable touch target. */
    .lc-cbtn { min-height: 2.5rem; }

    /* Participant grid: tighter gutters; clear the iOS home indicator. */
    .lc-voice-grid {
        gap: 0.75rem;
        padding: 0.75rem;
        padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
    }

    /* Transcript drawer becomes a bottom sheet: full width, anchored to the
       bottom, so it never covers the controls and respects the home-bar inset.
       (The right-padding reservation on #lc-voice-root is dropped here.) */
    body.lc-transcript-open #lc-voice-root { padding-right: 0; }
    .lc-transcript {
        top: auto;
        left: 0;
        right: 0;
        bottom: 0;
        width: 100%;
        max-width: 100%;
        height: 62vh;
        max-height: 80vh;
        border-left: none;
        border-top: 1px solid var(--border);
        border-radius: 0.875rem 0.875rem 0 0;
        box-shadow: 0 -10px 28px -14px rgba(0, 0, 0, 0.5);
    }
    .lc-transcript-body {
        padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
    }
}

/* LC-389: full categorized reaction picker. The scroll container holds labeled
   category sections; each section is an 8-column grid of .lc-emoji-cell. The
   sticky label keeps the current category visible while scrolling. */
.lc-emoji-scroll {
    max-height: 16rem;
    overflow-y: auto;
    padding: 0.25rem 0.5rem 0.5rem;
}
.lc-emoji-cat-label {
    position: sticky;
    top: 0;
    z-index: 1;
    /* LC-623: tighter vertical rhythm so the panel is not longer than it needs
       to be now that the redundant Recent row is gone. */
    padding: 0.1875rem 0.125rem 0.125rem;
    background-color: var(--surface-elevated);
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--content-subtle);
}
.lc-emoji-cat-grid {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 0.125rem;
}
/* The category tab strip: one representative glyph per section, horizontally
   scrollable so it never wraps. The active tab gets an accent underline. */
.lc-emoji-tabs {
    display: flex;
    gap: 0.125rem;
    overflow-x: auto;
    /* LC-623: trim the tab-strip padding to keep the header block compact. */
    padding: 0.125rem 0.5rem;
    border-bottom: 1px solid var(--border);
}
.lc-emoji-tab {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.625rem;
    border-radius: 0.375rem;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
    border-bottom: 2px solid transparent;
}
.lc-emoji-tab:hover {
    background-color: var(--surface-sunken);
}
.lc-emoji-tab[data-lc-active] {
    border-bottom-color: var(--accent);
}

/* Universal reduced-motion kill switch. Sets transition + animation duration
   to near-zero (not 0; some JS waits on `transitionend` and 0 means the event
   never fires in some browsers), keeps scroll behaviour instant. Tailwind's
   `motion-safe:` / `motion-reduce:` variants still work on top of this. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* ===== LC-426: Settings redesign ======================================== */
/* Two-column shell: sticky section nav + a centered, max-width content column.
   Reuses the .btn / .input / .card / .toast component vocabulary; only the
   settings-specific layout, toggle, status and toast-region live here. */
.lc-set-shell {
    max-width: 60rem;
    margin: 0 auto;
    padding: 1.5rem 1.25rem 4rem;
    display: grid;
    grid-template-columns: 13rem minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
}
.lc-set-nav {
    position: sticky;
    top: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}
.lc-set-tab {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    text-align: left;
    border-radius: 0.5rem;
    padding: 0.5rem 0.75rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--content-muted);
    background: transparent;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background-color 0.12s ease, color 0.12s ease;
}
.lc-set-tab:hover { background: var(--surface-sunken); color: var(--content); }
.lc-set-tab:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); }
.lc-set-tab[aria-selected="true"],
.lc-set-tab[aria-current="page"] {
    background: var(--accent-surface);
    border-color: var(--accent);
    color: var(--accent-surface-content);
}
.lc-set-tab svg { height: 1.05rem; width: 1.05rem; flex: none; }

/* LC-510: admin panel shell. A grouped left sidebar (reusing .lc-set-tab for
   the links) + a scrollable content column, replacing the overflowing
   horizontal tab strip. Collapses to a horizontal rail under 48rem. */
.lc-admin-shell {
    display: grid;
    grid-template-columns: 14rem minmax(0, 1fr);
    flex: 1 1 auto;
    height: 100%;
    min-height: 0;
}
.lc-admin-nav {
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--border);
    overflow-y: auto;
    padding: 0.75rem;
}
.lc-admin-nav-scroll {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    flex: 1 1 auto;
}
.lc-admin-group-label {
    margin: 0.875rem 0.75rem 0.25rem;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--content-subtle);
}
.lc-admin-group-label:first-child { margin-top: 0; }
.lc-admin-badge { margin-left: auto; }
.lc-admin-version {
    margin-top: 0.75rem;
    padding-top: 0.625rem;
    border-top: 1px solid var(--border);
    font-size: 0.6875rem;
    color: var(--content-subtle);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    white-space: nowrap;
}
.lc-admin-content {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
}
.lc-admin-header {
    border-bottom: 1px solid var(--border);
    padding: 0.625rem 1.25rem;
}
.lc-admin-title { font-size: 1rem; font-weight: 600; color: var(--content); }
/* LC-572: shared admin page-header. The bar sits the title/description block
   against an optional right-aligned action cluster and wraps on narrow widths;
   the filter/toolbar block (if any) renders on its own row under the bar. */
.lc-admin-header-bar {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}
.lc-admin-header-heading { min-width: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.lc-admin-desc { font-size: 0.875rem; line-height: 1.4; color: var(--content-muted); }
.lc-admin-header > .lc-toolbar { margin-top: 0.625rem; }
.lc-admin-main { flex: 1 1 auto; padding: 1.5rem 1.25rem 4rem; }
/* A redesigned page caps form content; tables stay full-width. */
.lc-admin-narrow { max-width: 48rem; margin-left: auto; margin-right: auto; display: flex; flex-direction: column; gap: 1.25rem; }

/* LC-510: shared admin table. Wrap in `.card.overflow-hidden` so rows go edge
   to edge; cells provide the padding. Reused across the admin list pages. */
.lc-table { width: 100%; font-size: 0.875rem; border-collapse: collapse; }
.lc-table thead th {
    text-align: left;
    font-weight: 500;
    color: var(--content-muted);
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
.lc-table tbody td {
    padding: 0.625rem 0.75rem;
    border-top: 1px solid var(--border);
    vertical-align: top;
}
.lc-table tbody tr:first-child td { border-top: 0; }
.lc-table tbody tr:hover { background: var(--surface-sunken); }
.lc-table-actions { text-align: right; white-space: nowrap; }
/* Compact danger / caution outline buttons for destructive row actions. */
.btn-danger-outline {
    color: var(--danger);
    border: 1px solid var(--danger-border);
    background: transparent;
}
.btn-danger-outline:hover { background: var(--danger-surface); }
.btn-warning-outline {
    color: var(--warning);
    border: 1px solid var(--warning-border);
    background: transparent;
}
.btn-warning-outline:hover { background: var(--warning-surface); }
@media (max-width: 48rem) {
    .lc-admin-shell { grid-template-columns: minmax(0, 1fr); }
    .lc-admin-nav {
        flex-direction: row;
        align-items: center;
        gap: 0.25rem;
        border-right: none;
        border-bottom: 1px solid var(--border);
        overflow-x: auto;
    }
    .lc-admin-nav-scroll { flex-direction: row; align-items: center; gap: 0.25rem; }
    .lc-admin-group-label { display: none; }
    .lc-admin-version { display: none; }
}
.lc-set-main { min-width: 0; display: flex; flex-direction: column; gap: 1.25rem; }
.lc-set-panel { display: flex; flex-direction: column; gap: 1.25rem; }
.lc-set-panel[hidden] { display: none; }
.lc-set-panel-title { font-size: 1.25rem; font-weight: 700; color: var(--content); }

/* Settings card: a .card with internal structure. */
.lc-set-card { padding: 1.25rem 1.25rem 1.375rem; }
.lc-set-card--danger { border-color: var(--danger-border); background: var(--danger-surface); }
.lc-set-card-head { margin-bottom: 1rem; }
.lc-set-card-title { font-size: 1rem; font-weight: 600; color: var(--content); }
.lc-set-card-desc { margin-top: 0.125rem; font-size: 0.8125rem; color: var(--content-muted); }
.lc-set-pending { color: var(--warning); font-weight: 500; }

/* Field: label + control + help, evenly spaced. */
.lc-set-form { display: flex; flex-direction: column; gap: 1rem; }
.lc-set-field { display: flex; flex-direction: column; gap: 0.3125rem; }
.lc-set-field.lc-set-field--narrow { max-width: 22rem; }
.lc-set-label { font-size: 0.8125rem; font-weight: 500; color: var(--content); }
.lc-set-help { font-size: 0.75rem; color: var(--content-muted); }
/* LC-553: segmented control for enumerated prefs (theme mode, scaling, density),
   matching the prototype's settings pills. A radiogroup of sr-only radios wrapped
   by label pills; :has(:checked) fills the active pill with the accent. */
.lc-set-seg {
    display: inline-flex;
    gap: 0.125rem;
    padding: 0.1875rem;
    border-radius: 0.625rem;
    background-color: var(--surface-sunken);
    border: 1px solid var(--border);
    align-self: flex-start;
    max-width: 100%;
    flex-wrap: wrap;
}
.lc-set-seg-opt {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.375rem 0.75rem;
    border-radius: 0.4375rem;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--content-muted);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 0.12s ease, color 0.12s ease;
}
.lc-set-seg-opt:hover { color: var(--content); }
.lc-set-seg-opt:has(:checked) {
    background-color: var(--accent);
    color: var(--accent-content);
}
.lc-set-seg-opt:has(:focus-visible) { box-shadow: 0 0 0 2px var(--ring); }
/* LC-439: inline field validation error (avatar size/type). */
.lc-set-error { font-size: 0.75rem; font-weight: 500; color: var(--danger); }
.lc-set-error[hidden] { display: none; }
textarea.input { min-height: 4.5rem; resize: vertical; }

/* Action row: submit button + inline status, with a spinner inside the button. */
.lc-set-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    padding-top: 0.25rem;
}
/* LC-432: push a trailing action (Remove avatar) to the far right so the status
   stays anchored next to Save. */
.lc-set-actions-end { margin-left: auto; }
.lc-set-actions .lc-spinner.htmx-indicator { display: none; }
.htmx-request .lc-spinner.htmx-indicator { display: inline-block; }
/* Inline per-form status, swapped in by the server fragment. */
.lc-set-status:empty { display: none; }
.lc-status {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.8125rem;
    font-weight: 500;
}
.lc-status--ok { color: var(--success); }
.lc-status--err { color: var(--danger); }
.lc-status-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 1rem;
    width: 1rem;
    border-radius: 9999px;
    font-size: 0.6875rem;
    line-height: 1;
    color: var(--accent-content);
}
.lc-status--ok .lc-status-ico { background: var(--success); }
.lc-status--err .lc-status-ico { background: var(--danger); color: var(--danger-content); }

/* Definition lists (Account / About). */
.lc-set-defs { display: flex; flex-direction: column; gap: 0.375rem; font-size: 0.875rem; }
.lc-set-defs > div { display: flex; gap: 0.75rem; }
.lc-set-defs dt { width: 8rem; flex: none; color: var(--content-muted); }
.lc-set-defs dd { color: var(--content); min-width: 0; word-break: break-word; }

/* Toggle switch: a styled checkbox + label + description, aligned in a row. */
.lc-toggle {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    cursor: pointer;
}
.lc-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.lc-toggle-track {
    position: relative;
    flex: none;
    margin-top: 0.125rem;
    height: 1.25rem;
    width: 2.25rem;
    border-radius: 9999px;
    background: var(--surface-sunken);
    border: 1px solid var(--border-strong);
    transition: background-color 0.15s ease, border-color 0.15s ease;
}
.lc-toggle-track::after {
    content: "";
    position: absolute;
    top: 0.125rem;
    left: 0.125rem;
    height: 0.875rem;
    width: 0.875rem;
    border-radius: 9999px;
    background: var(--content-muted);
    transition: transform 0.15s ease, background-color 0.15s ease;
}
.lc-toggle input:checked + .lc-toggle-track {
    background: var(--accent);
    border-color: var(--accent);
}
.lc-toggle input:checked + .lc-toggle-track::after {
    transform: translateX(1rem);
    background: var(--accent-content);
}
.lc-toggle input:focus-visible + .lc-toggle-track { box-shadow: 0 0 0 2px var(--ring); }
.lc-toggle input:disabled + .lc-toggle-track { opacity: 0.5; }
.lc-toggle:has(input:disabled) { cursor: not-allowed; opacity: 0.7; }
.lc-toggle-text { min-width: 0; }
.lc-toggle-title { font-size: 0.875rem; color: var(--content); }
.lc-toggle-desc { display: block; font-size: 0.75rem; color: var(--content-muted); margin-top: 0.125rem; }

/* Quiet-hours time grid: labelled start/end pairs, aligned. */
.lc-set-timegrid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem 1rem;
    max-width: 30rem;
}
.lc-set-timegrid input.input { color-scheme: light dark; }

/* Avatar editor row. */
.lc-set-avatar-row { display: flex; align-items: flex-start; gap: 1rem; }
.lc-set-avatar {
    height: 4rem; width: 4rem; flex: none;
    border-radius: 9999px;
    object-fit: cover;
    border: 1px solid var(--border);
}
.lc-set-avatar-fallback {
    display: flex; align-items: center; justify-content: center;
    height: 4rem; width: 4rem; flex: none;
    border-radius: 9999px;
    background: var(--surface-sunken);
    color: var(--content-muted);
    font-weight: 600; font-size: 1.25rem;
}

/* Sessions list. */
.lc-set-sessions { display: flex; flex-direction: column; }
.lc-set-session {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 1rem;
    padding: 0.75rem 0;
    border-top: 1px solid var(--border);
}
.lc-set-session:first-child { border-top: none; }

/* ===== LC-426: toast region ============================================= */
.lc-toast-region {
    position: fixed;
    z-index: 80;
    right: 1rem;
    bottom: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-width: min(22rem, calc(100vw - 2rem));
    pointer-events: none;
}
.lc-toast {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    border-left-width: 3px;
    background: var(--surface-elevated);
  color: var(--content);
    color: var(--content);
    padding: 0.625rem 0.75rem;
    font-size: 0.8125rem;
    box-shadow: 0 8px 24px -10px rgba(0, 0, 0, 0.45);
    pointer-events: auto;
    animation: lc-toast-in 160ms ease-out;
}
.lc-toast--ok { border-left-color: var(--success); }
.lc-toast--err { border-left-color: var(--danger); }
.lc-toast.lc-toast--leaving { animation: lc-toast-out 160ms ease-in forwards; }
.lc-toast-ico {
    display: inline-flex; align-items: center; justify-content: center;
    flex: none; height: 1.125rem; width: 1.125rem; margin-top: 0.0625rem;
    border-radius: 9999px;
    font-size: 0.75rem; line-height: 1;
    color: var(--accent-content);
}
.lc-toast--ok .lc-toast-ico { background: var(--success); }
.lc-toast--err .lc-toast-ico { background: var(--danger); color: var(--danger-content); }
.lc-toast-msg { flex: 1; min-width: 0; }
.lc-toast-close {
    flex: none;
    margin-left: 0.25rem;
    border: none; background: transparent; cursor: pointer;
    color: var(--content-subtle);
    font-size: 1rem; line-height: 1;
    padding: 0 0.125rem;
}
.lc-toast-close:hover { color: var(--content); }
@keyframes lc-toast-in { from { opacity: 0; transform: translateY(0.5rem); } to { opacity: 1; transform: none; } }
@keyframes lc-toast-out { to { opacity: 0; transform: translateY(0.5rem); } }

/* Mobile: nav becomes a horizontal scroll strip above the content. */
@media (max-width: 768px) {
    .lc-set-shell { grid-template-columns: minmax(0, 1fr); padding: 1rem 0.875rem 3rem; gap: 1rem; }
    .lc-set-nav {
        position: static;
        flex-direction: row;
        gap: 0.375rem;
        overflow-x: auto;
        padding-bottom: 0.25rem;
        scrollbar-width: thin;
    }
    .lc-set-tab { width: auto; flex: none; white-space: nowrap; }
    .lc-set-timegrid { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
    .lc-toast, .lc-toast.lc-toast--leaving { animation: none; }
    .lc-toggle-track, .lc-toggle-track::after { transition: none; }
}

/* ===== LC-440: Call transcripts list ==================================== */
.lc-tx-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    background: var(--surface-elevated);
  color: var(--content);
    padding: 0.5rem 0.75rem;
    transition: background-color 0.12s ease, border-color 0.12s ease;
}
.lc-tx-row:hover { background: var(--surface-sunken); }
.lc-tx-row:focus-within { border-color: var(--accent); }
/* Stretched link: covers the row so the whole card is clickable; the delete
   button sits above it (z-index) so the two never conflict. */
.lc-tx-link { position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
.lc-tx-link:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); }
.lc-tx-main { min-width: 0; flex: 1; }
.lc-tx-titleline { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.375rem 0.5rem; }
.lc-tx-title {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--content);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}
.lc-tx-meta {
    margin-top: 0.125rem;
    font-size: 0.75rem;
    color: var(--content-muted);
}
/* Type badge: distinct subtle colours so DM vs voice scan at a glance. */
.lc-tx-badge {
    flex: none;
    border-radius: 9999px;
    padding: 0.0625rem 0.5rem;
    font-size: 0.6875rem;
    font-weight: 600;
    line-height: 1.1;
}
.lc-tx-badge--dm { background: var(--accent-surface); color: var(--accent-surface-content); }
.lc-tx-badge--voice { background: var(--success-surface); color: var(--success); }
.lc-tx-live { flex: none; font-size: 0.6875rem; font-weight: 600; color: var(--danger); }
/* 0-line rows are de-emphasized so they don't compete with real transcripts. */
.lc-tx-row--empty { background: transparent; }
.lc-tx-row--empty .lc-tx-title { color: var(--content-muted); font-weight: 500; }
.lc-tx-row--empty .lc-tx-meta { color: var(--content-subtle); }
.lc-tx-row--empty:hover { background: var(--surface-sunken); }
/* Per-row delete: hover/focus-revealed, above the stretched link. */
.lc-tx-del {
    position: relative;
    z-index: 2;
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 1.75rem;
    width: 1.75rem;
    border-radius: 0.375rem;
    color: var(--content-subtle);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.12s ease, background-color 0.12s ease, color 0.12s ease;
}
.lc-tx-row:hover .lc-tx-del,
.lc-tx-row:focus-within .lc-tx-del,
.lc-tx-del:focus-visible { opacity: 1; }
.lc-tx-del:hover { background: var(--danger-surface); color: var(--danger); }
.lc-tx-del svg { height: 1rem; width: 1rem; }
.lc-tx-chevron { flex: none; height: 1rem; width: 1rem; color: var(--content-subtle); }
/* Hide-empty toggle label. */
.lc-tx-hide {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.75rem;
    color: var(--content-muted);
    cursor: pointer;
    user-select: none;
}
/* Friendly empty state. */
.lc-tx-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 3rem 1rem;
    text-align: center;
    color: var(--content-muted);
}
.lc-tx-empty-ico { height: 2.5rem; width: 2.5rem; color: var(--content-subtle); }
.lc-tx-empty-title { font-size: 0.875rem; color: var(--content-muted); max-width: 22rem; }
@media (prefers-reduced-motion: reduce) {
    .lc-tx-row, .lc-tx-del { transition: none; }
}

/* LC-454: shared room About / Manage polish.
   Reuses the .lc-set-* / .card / .btn vocabulary; adds a single empty-state
   treatment and a "caution" card variant (less severe than --danger) for the
   message-retention section. */
.lc-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    text-align: center;
    padding: 1.5rem 1rem;
}
.lc-empty-ico { height: 2rem; width: 2rem; color: var(--content-subtle); }
.lc-empty-text { font-size: 0.8125rem; color: var(--content-muted); max-width: 24rem; }
/* Caution card: amber edge for the destructive-but-not-irreversible retention
   section, distinct from the red --danger delete card. */
.lc-set-card--caution { border-color: var(--warning-border); }
/* State pill (retention current state). */
.lc-pill {
    display: inline-flex;
    align-items: center;
    border-radius: 9999px;
    padding: 0.0625rem 0.5rem;
    font-size: 0.6875rem;
    font-weight: 600;
    line-height: 1.4;
}
.lc-pill--off { background: var(--surface-sunken); color: var(--content-muted); }
.lc-pill--on { background: var(--danger-surface); color: var(--danger); }
/* LC-561: semantic status-chip variants on the shared pill, so admin tables
   (bridge health, ban / mute state, bot enablement) render one consistent chip
   vocabulary instead of three ad-hoc idioms. `--on` / `--off` stay for the
   retention toggle pill. Danger has no `-surface-content` token (LC-215), so
   the danger chip takes `--danger` for its text, matching `.alert-danger`. */
.lc-pill--neutral { background: var(--surface-sunken); color: var(--content-muted); }
.lc-pill--success { background: var(--success-surface); color: var(--success-surface-content); }
.lc-pill--warning { background: var(--warning-surface); color: var(--warning-surface-content); }
.lc-pill--danger  { background: var(--danger-surface); color: var(--danger); }

/* LC-479: /saved label filter chips. Toggle button styled as a pill; the
   active chip (aria-pressed=true) uses the accent surface. */
.lc-saved-chip {
    display: inline-flex;
    align-items: center;
    border-radius: 9999px;
    border: 1px solid var(--border);
    padding: 0.0625rem 0.5rem;
    font-size: 0.6875rem;
    font-weight: 600;
    line-height: 1.4;
    color: var(--content-muted);
    background: var(--surface-sunken);
}
.lc-saved-chip:hover { background: var(--surface-elevated); color: var(--content); }
.lc-saved-chip[aria-pressed="true"] {
    background: var(--accent-surface);
    color: var(--accent-surface-content);
    border-color: var(--accent);
}

/* LC-463: toggle switch for enclave on/off settings. Rendered as a submit
   button (so the existing POST-flip server contract is unchanged); aria-checked
   drives the visual on/off state, set by the server from the current value. */
.lc-switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    height: 1.5rem;
    width: 2.75rem;
    flex: none;
    border: none;
    border-radius: 9999px;
    background: var(--border-strong);
    cursor: pointer;
    transition: background-color 0.15s ease;
}
.lc-switch[aria-checked="true"] { background: var(--accent); }
.lc-switch:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); }
.lc-switch-thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    height: calc(1.5rem - 4px);
    width: calc(1.5rem - 4px);
    border-radius: 9999px;
    background: #ffffff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
    transition: transform 0.15s ease;
}
.lc-switch[aria-checked="true"] .lc-switch-thumb { transform: translateX(1.25rem); }
@media (prefers-reduced-motion: reduce) {
    .lc-switch, .lc-switch-thumb { transition: none; }
}
/* A settings row that pairs explanatory copy with a trailing control. */
.lc-set-toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

/* LC-469: branding color swatch - style the native color input as a clean,
   consistently-sized swatch matching the form controls. */
.lc-swatch {
    height: 2.25rem;
    width: 2.75rem;
    flex: none;
    padding: 0;
    border: 1px solid var(--border-strong);
    border-radius: 0.375rem;
    background: transparent;
    cursor: pointer;
}
.lc-swatch::-webkit-color-swatch-wrapper { padding: 3px; }
.lc-swatch::-webkit-color-swatch { border: none; border-radius: 0.25rem; }
.lc-swatch::-moz-color-swatch { border: none; border-radius: 0.25rem; }

/* LC-569: room-info hero badge (matches the prototype's accent-surface hash tile). */
.lc-info-badge {
    flex: none;
    display: grid;
    place-items: center;
    width: 3.25rem;
    height: 3.25rem;
    border-radius: 0.875rem;
    background-color: var(--accent-surface);
    color: var(--accent-surface-content);
    font-size: 1.5rem;
    font-weight: 700;
}

/* LC-611: huddle ring banner. Bottom-centre so it does not collide with the
   toast region (top-right) or the call bar, and above the transcript drawer. */
.lc-huddle-ring {
  position: fixed;
  bottom: 1.25rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  max-width: min(32rem, calc(100vw - 2rem));
  padding: 0.625rem 0.875rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--surface-elevated);
  color: var(--content);
  box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
}

.lc-huddle-ring-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.875rem;
}

@media (prefers-reduced-motion: no-preference) {
  .lc-huddle-ring { animation: lc-huddle-ring-in 160ms ease-out; }
}

@keyframes lc-huddle-ring-in {
  from { opacity: 0; transform: translate(-50%, 0.5rem); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
