@layer reset, base, components, chat, utilities, ios;

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  html, body { margin: 0; min-height: 100%; }
  button, input, textarea, select { font: inherit; }
  img, svg { display: block; max-width: 100%; }
  [hidden] { display: none !important; }
  turbo-cable-stream-source { display: none; }
}

@layer base {
  :root {
    --ink: oklch(23% 0 0);
    --muted: oklch(50% 0 0);
    --paper: oklch(97.5% 0 0);
    --surface: oklch(100% 0 0);
    --line: oklch(90% 0 0);
    --accent: oklch(55% 0.13 267);
    --accent-soft: oklch(95% 0.02 267);
    --danger: oklch(54% 0.18 27);
    /* The unread avatar ring. Fixed, not theme-derived: unread must read the
       same whichever chat theme the family picked. */
    --unread-ring: linear-gradient(20deg, #facf34, #fa7d1e, #e32e47);
    --radius: 0.55rem;
    --ios-separator: rgb(60 60 67 / 0.29);
    color-scheme: light;
  }

  /* Dark mode swaps token values only; every component color derives from
     these, so no per-component overrides belong here. Surface stays the
     raised layer (lighter than paper) in both schemes. */
  @media (prefers-color-scheme: dark) {
    :root {
      --ink: oklch(92% 0 0);
      --muted: oklch(66% 0 0);
      --paper: oklch(0% 0 0);
      --surface: oklch(21.5% 0.005 267);
      --line: oklch(30% 0 0);
      --accent: oklch(66% 0.12 267);
      --accent-soft: oklch(30% 0.04 267);
      --danger: oklch(64% 0.17 27);
      --ios-separator: rgb(84 84 88 / 0.6);
      color-scheme: dark;
    }
  }

  body {
    background: var(--paper);
    color: var(--ink);
    font-family: system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
    line-height: 1.5;
  }

  a { color: var(--accent); text-decoration: none; }
  @media (hover: hover) and (pointer: fine) { a:hover { text-decoration: underline; } }
  h1, h2, h3, p { margin-block-start: 0; }
  h1 { font-size: clamp(1.7rem, 4vw, 2.35rem); letter-spacing: -0.03em; }
  h2 { font-size: 1.15rem; }
  label { display: block; font-weight: 650; margin-bottom: 0.3rem; }
  input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]), textarea, select {
    width: 100%;
    border: 1px solid var(--line);
    border-radius: 0.65rem;
    background: var(--surface);
    color: var(--ink);
    padding: 0.68rem 0.78rem;
  }
  textarea { min-height: 8rem; resize: vertical; }
  input:focus, textarea:focus, select:focus { outline: 2px solid color-mix(in oklch, var(--accent) 35%, transparent); border-color: var(--accent); }
}

@layer components {
  .page-shell { min-height: 100dvh; }
  .topbar {
    align-items: center; background: color-mix(in oklch, var(--surface) 92%, transparent);
    border-bottom: 1px solid var(--line); display: grid; grid-template-columns: 2.5rem 1fr 2.5rem;
    min-height: 3.5rem; padding: 0.5rem max(1rem, env(safe-area-inset-left)); position: sticky; text-align: center; top: 0; z-index: 20;
    backdrop-filter: blur(16px);
  }
  .topbar-title { color: var(--ink); font-size: 1rem; font-weight: 700; margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .menu-button { align-items: center; background: transparent; border: 0; border-radius: 50%; color: var(--ink); display: flex; height: 2.5rem; justify-content: center; width: 2.5rem; }
  .topbar-back:hover { text-decoration: none; }
  .list-create-action { align-items: center; background: transparent; border: 0; border-radius: 50%; color: var(--accent); display: flex; height: 2.5rem; justify-content: center; width: 2.5rem; }
  .list-create-action:hover { background: color-mix(in oklch, var(--ink) 6%, transparent); text-decoration: none; }
  .list-create-action svg { height: 1.35rem; width: 1.35rem; }
  .page-content { margin: 0 auto; max-width: 58rem; padding: 2rem 1rem 4rem; width: 100%; }
  .auth-content { margin: 16vh auto 0; max-width: 25rem; padding: 1rem; }
  .page-heading { margin-bottom: 1.5rem; }
  .page-actions { margin-top: 2rem; }
  .fab-actions { align-items: center; bottom: calc(1.25rem + env(safe-area-inset-bottom)); display: flex; gap: 0.65rem; left: 50%; position: fixed; transform: translateX(-50%); z-index: 30; }
  .fab-actions .menu-action { height: 3rem; width: 3rem; }
  .page-heading p, .page-intro, .muted { color: var(--muted); }
  .page-intro { margin-bottom: 1.5rem; }
  .stack { display: grid; gap: 1rem; }
  .stack-tight { display: grid; gap: 0.5rem; }
  .split { align-items: center; display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: space-between; }
  .grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
  .card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.15rem; }
  .card > :last-child { margin-bottom: 0; }
  /* A filled control wears the theme's own bubble colour and its ink. The
     text accent is a different job — it has to stay dark enough to read as
     text, which on a marigold theme means a brown nobody wants on a button. */
  .button, input[type="submit"] {
    align-items: center; background: var(--c-own-bubble, var(--accent)); border: 0; border-radius: 0.65rem;
    color: var(--c-own-bubble-ink, white);
    cursor: pointer; display: inline-flex; font-weight: 700; justify-content: center; min-height: 2.65rem; padding: 0.55rem 0.95rem;
  }
  .button:hover { color: var(--c-own-bubble-ink, white); text-decoration: none; filter: brightness(0.95); }
  .button.secondary { background: var(--c-accent-soft, var(--accent-soft)); color: var(--c-accent, var(--accent)); }
  .button.danger { background: color-mix(in oklch, var(--danger) 12%, var(--surface)); color: var(--danger); }
  .button.full { width: 100%; }
  /* Turbo confirmations (application.js). */
  .confirm-dialog { background: var(--c-bg, var(--surface)); border: 0; border-radius: 1.1rem; box-shadow: 0 20px 60px rgb(0 0 0 / 0.3); color: var(--c-ink, var(--ink)); max-width: min(22rem, calc(100vw - 2rem)); padding: 1.25rem 1.25rem 1rem; }
  .confirm-dialog::backdrop { -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); background: rgb(0 0 0 / 0.35); }
  .confirm-dialog[open] { animation: menu-pop 160ms ease both; }
  .confirm-dialog p { font-size: 1rem; line-height: 1.45; margin: 0 0 1.1rem; text-wrap: pretty; }
  .confirm-dialog-actions { display: grid; gap: 0.5rem; grid-template-columns: 1fr 1fr; }
  .confirm-dialog-actions button { border: 0; border-radius: 0.75rem; cursor: pointer; font-size: 0.97rem; font-weight: 650; min-height: 2.75rem; }
  .confirm-dialog-cancel { background: color-mix(in oklch, var(--c-ink, var(--ink)) 8%, transparent); color: var(--c-ink, var(--ink)); }
  .confirm-dialog-confirm { background: var(--c-own-bubble, var(--accent)); color: var(--c-own-bubble-ink, white); }
  .confirm-dialog-danger { background: var(--danger); color: white; }
  @media (max-width: 640px) {
    .confirm-dialog { border-radius: 1.25rem; margin: auto auto max(0.75rem, env(safe-area-inset-bottom)); max-width: calc(100vw - 1.5rem); width: 100%; }
    .confirm-dialog[open] { animation: sheet-rise 200ms ease both; }
    .confirm-dialog-actions { grid-template-columns: 1fr; }
    .confirm-dialog-actions button { min-height: 3rem; }
    .confirm-dialog-confirm { order: -1; }
  }
  @keyframes sheet-rise { from { transform: translateY(1.5rem); opacity: 0; } }

  /* The iOS switch: a button that posts the other state, so it needs no script. */
  .switch-row { align-items: center; display: flex; gap: 1rem; justify-content: space-between; }
  .switch-row p { font-size: 0.88rem; margin: 0.15rem 0 0; }
  .switch { background: color-mix(in oklch, var(--ink) 16%, transparent); border: 0; border-radius: 999px; cursor: pointer; flex: none; height: 1.95rem; padding: 0.125rem; transition: background 160ms ease; width: 3.2rem; }
  .switch span { background: white; border-radius: 50%; box-shadow: 0 2px 5px rgb(0 0 0 / 0.2); display: block; height: 1.7rem; transition: transform 160ms ease; width: 1.7rem; }
  .switch[aria-checked="true"] { background: oklch(72% 0.2 145); }
  .switch[aria-checked="true"] span { transform: translateX(1.25rem); }
  .button_to { display: inline; }
  .form-row { display: grid; gap: 0.35rem; }
  .room-member { align-items: center; display: flex; gap: 0.6rem; margin: 0; padding: 0.45rem 0.25rem; }
  .checkbox-row { align-items: center; display: flex; gap: 0.5rem; }
  .checkbox-row label { margin: 0; }
  .flash { border: 1px solid var(--line); border-radius: 0.65rem; margin-bottom: 1rem; padding: 0.7rem 0.9rem; }
  .flash.notice { background: var(--accent-soft); color: var(--accent); }
  .flash.alert { background: color-mix(in oklch, var(--danger) 10%, var(--surface)); color: var(--danger); }
  .status { border-radius: 999px; display: inline-block; font-size: 0.78rem; font-weight: 700; padding: 0.15rem 0.5rem; background: var(--accent-soft); color: var(--accent); }
  .status-private { align-items: center; background: color-mix(in oklch, var(--ink) 7%, transparent); color: var(--muted); display: inline-flex; gap: 0.25rem; }
  .status-lock { height: 0.8rem; width: 0.8rem; }
  .record { border-bottom: 1px solid var(--line); padding-block: 0.9rem; }
  .record:last-child { border-bottom: 0; }
  .status-quiet { background: color-mix(in oklch, var(--ink) 7%, transparent); color: var(--muted); }
  .status-warn { background: color-mix(in oklch, var(--danger) 12%, var(--surface)); color: var(--danger); }
  /* Administration: user reports. A table on a wide screen, a stack of rows
     on a phone, each row still one report with its dates. */
  .report-list { padding-block: 0.25rem; }
  .report-table { border-collapse: collapse; font-size: 0.95rem; width: 100%; }
  .report-table th, .report-table td { border-bottom: 1px solid var(--line); padding: 0.9rem 0.75rem 0.9rem 0; text-align: left; vertical-align: top; }
  .report-table th { color: var(--muted); font-size: 0.78rem; font-weight: 700; letter-spacing: 0.04em; padding-block: 0.75rem 0.6rem; text-transform: uppercase; }
  .report-table th:first-child { width: 50%; }
  .report-table tr:last-child td { border-bottom: 0; }
  .report-table .right { padding-right: 0; text-align: right; }
  .report-title { align-items: baseline; display: flex; font-weight: 600; gap: 0.5rem; }
  .report-number { flex: none; font-variant-numeric: tabular-nums; }
  .report-meta { align-items: center; display: flex; flex-wrap: wrap; font-size: 0.85rem; gap: 0.5rem; margin-top: 0.2rem; }
  .stamp { font-variant-numeric: tabular-nums; white-space: nowrap; }
  .report-footer { font-size: 0.9rem; margin-top: 1.5rem; }
  .report-heading { display: grid; gap: 0.5rem; }
  .report-heading h1 { margin: 0; text-wrap: pretty; }
  .report-back { font-size: 0.9rem; font-weight: 600; }
  .report-reviewed { font-size: 0.9rem; }
  .facts { border-bottom: 1px solid var(--line); display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); margin: 0 0 1.15rem; padding-bottom: 1.15rem; }
  .facts div { display: grid; gap: 0.125rem; }
  .facts dt { color: var(--muted); font-size: 0.78rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
  .facts dd { font-weight: 600; margin: 0; }
  .comment { display: flex; gap: 1rem; }
  .comment-author { display: grid; flex: 0 0 8.75rem; font-size: 0.85rem; gap: 0.125rem; }
  .comment-author strong { font-size: 1rem; }
  .comment-body { flex: 1; }
  .comment-body p:last-child { margin-bottom: 0; }
  .form-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); }
  .review-hint { font-size: 0.9rem; margin: 0; }
  @media (max-width: 40rem) {
    .report-table thead { display: none; }
    .report-table tr { border-bottom: 1px solid var(--line); display: grid; gap: 0 0.5rem; grid-template-columns: minmax(0, 1fr) auto; padding-block: 0.9rem; }
    .report-table tr:last-child { border-bottom: 0; }
    .report-table td { border-bottom: 0; grid-column: 1 / -1; padding: 0; }
    .report-table td:first-child { grid-column: 1; grid-row: 1; }
    .report-table td.right { grid-column: 2; grid-row: 1; }
    .report-table td[data-label] { color: var(--muted); font-size: 0.85rem; }
    .report-table td[data-label]::before { content: attr(data-label) " "; }
    .comment { flex-direction: column; gap: 0.25rem; }
    .comment-author { align-items: baseline; display: flex; flex: none; gap: 0.5rem; }
  }
  .actions { align-items: center; display: flex; flex-wrap: wrap; gap: 0.5rem; }
  .photo-crop { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); color: var(--ink); max-width: min(22rem, 92vw); padding: 1.15rem; }
  .photo-crop::backdrop { background: rgb(0 0 0 / 55%); }
  .photo-crop-hint { color: var(--muted); margin: 0 0 0.75rem; }
  .photo-crop-canvas { aspect-ratio: 1; background: var(--paper); border-radius: var(--radius); cursor: grab; display: block; touch-action: none; width: 100%; }
  .photo-crop-canvas:active { cursor: grabbing; }
  .photo-crop-zoom { display: block; margin-block: 0.9rem; width: 100%; }
  .photo-crop .actions { justify-content: flex-end; }
  .guide { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); margin-inline: auto; max-width: 50rem; padding: clamp(1.25rem, 4vw, 3rem); }
  .guide h1 { margin-bottom: 1.75rem; }
  .guide h2 { border-top: 1px solid var(--line); margin: 2.5rem 0 0.85rem; padding-top: 1.5rem; }
  .guide h3 { margin: 1.75rem 0 0.65rem; }
  .guide ul, .guide ol { padding-inline-start: 1.5rem; }
  .guide li + li { margin-top: 0.3rem; }
  .guide table { border-collapse: collapse; display: block; margin-block: 1.25rem; overflow-x: auto; width: 100%; }
  .guide th, .guide td { border: 1px solid var(--line); min-width: 8rem; padding: 0.55rem 0.7rem; text-align: left; vertical-align: top; }
  .guide th { background: var(--accent-soft); }
  .guide pre { background: var(--paper); border: 1px solid var(--line); border-radius: 0.5rem; padding: 0.85rem; }
  .guide code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.9em; }
  .guide :not(pre) > code { background: var(--paper); border-radius: 0.3rem; padding: 0.1rem 0.3rem; }
  .guide blockquote { border-inline-start: 0.25rem solid var(--accent); color: var(--muted); margin-inline: 0; padding-inline-start: 1rem; }

  /* Family switcher: details is the web fallback. Stimulus consumes the
     first outside click while the menu is open. */
  .family-switcher { position: relative; }
  .family-switcher-trigger { align-items: center; background: transparent; border: 0; border-radius: 0.6rem; color: var(--ink); cursor: pointer; display: flex; font-size: 1.02rem; font-weight: 700; gap: 0.5rem; margin-bottom: 0.5rem; padding: 0.35rem 0.6rem; text-align: left; width: 100%; }
  .family-switcher-trigger::-webkit-details-marker { display: none; }
  .family-switcher-trigger::marker { content: ""; }
  .family-switcher-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .family-switcher-status, .family-menu-status { align-items: center; display: flex; gap: 0.45rem; margin-left: auto; }
  .family-switcher-chevron { color: var(--muted); flex: 0 0 1rem; height: 1rem; width: 1rem; }
  .family-unread-dot { background: var(--accent); border-radius: 50%; display: block; height: 0.5rem; width: 0.5rem; }
  .family-icon { color: inherit; flex: none; }
  .rail .family-switcher .family-icon { height: 1.15rem; opacity: 0.75; width: 1.15rem; }
  .family-avatar { align-items: center; background: oklch(58% 0.12 var(--avatar-h, 267)); border-radius: 50%; color: white; display: inline-flex; flex: 0 0 1.8rem; font-size: 0.85rem; font-weight: 600; height: 1.8rem; justify-content: center; width: 1.8rem; }
  .family-menu { background: var(--surface); border: 1px solid var(--line); border-radius: 0.75rem; box-shadow: 0 12px 32px rgb(0 0 0 / 0.18); display: none; left: 0; max-height: calc(100dvh - 5rem); overflow-y: auto; padding: 0.4rem; position: absolute; top: calc(100% + 0.25rem); width: min(calc(100vw - 1.5rem), 17rem); z-index: 41; }
  .family-switcher[open] .family-menu { display: block; }
  .family-switcher[open] .family-switcher-chevron { transform: rotate(180deg); }
  .family-menu .button_to { display: block; margin: 0; width: 100%; }
  .family-menu-row { align-items: center; background: transparent; border: 0; border-radius: 0.5rem; color: var(--ink); cursor: pointer; display: flex; font-size: 0.95rem; gap: 0.55rem; padding: 0.5rem 0.6rem; text-align: left; width: 100%; }
  .family-menu-row > .family-icon { flex: 0 0 1.8rem; height: 1.8rem; width: 1.8rem; }
  .family-menu-row.current { font-weight: 700; }
  .family-menu-check { color: var(--accent); flex: 0 0 1rem; height: 1rem; width: 1rem; }
  .family-menu-plus { align-items: center; background: color-mix(in oklch, var(--ink) 6%, transparent); border-radius: 50%; display: inline-flex; flex: 0 0 1.8rem; height: 1.8rem; justify-content: center; width: 1.8rem; }
  .family-menu-plus svg { height: 1rem; width: 1rem; }
  .family-menu-divider { border-top: 1px solid var(--line); margin: 0.35rem 0.25rem; }
  @media (hover: hover) and (pointer: fine) {
    .family-switcher-trigger:hover { background: color-mix(in oklch, var(--ink) 5%, transparent); }
    .family-menu-row:hover { background: color-mix(in oklch, var(--ink) 6%, transparent); text-decoration: none; }
  }

  /* Family membership faces are reused in Settings. */
  .family-row-avatars { display: flex; }
  .family-row-avatars .member-avatar { border: 2px solid var(--paper); font-size: 1.05rem; height: 2.6rem; width: 2.6rem; }
  .family-row-avatars .member-avatar + .member-avatar { margin-left: -0.7rem; }
  .member-avatar-photo { object-fit: cover; padding: 0; }
  .member-avatar.member-avatar-lg { flex: none; font-size: 1.5rem; height: 4rem; width: 4rem; }
  /* A face is large when it is the subject of the screen and small when it
     is the marker on a row. Both sizes carry the same editor. */
  .settings-identity .family-member-avatar, .settings-identity .family-avatar { flex: none; font-size: 1.6rem; height: 4.5rem; width: 4.5rem; }
  .settings-row .family-member-avatar, .settings-row .member-avatar.settings-avatar { border: 0; flex: none; font-size: 0.95rem; height: 2.2rem; width: 2.2rem; }
  .settings-field textarea { min-height: 4.5rem; }
  .settings-row .avatar-edit-badge { border-width: 1px; bottom: -0.2rem; height: 1.05rem; right: -0.2rem; width: 1.05rem; }
  .settings-row .avatar-edit-badge svg { height: 0.62rem; width: 0.62rem; }
  .settings-row .avatar-clear { height: 1.05rem; top: -0.2rem; width: 1.05rem; }
  .settings-row .avatar-clear svg { height: 0.6rem; width: 0.6rem; }

  /* The avatar is its own editor: tap it to pick a photo (the input hides
     behind the label), the camera badge is the hint, the ✕ clears. */
  .avatar-editor { align-items: center; display: flex; flex: none; gap: 0.65rem; }
  .avatar-stack { flex: none; position: relative; }
  .avatar-status { color: var(--muted); font-size: 0.8rem; max-width: 8rem; }
  .avatar-status:empty { display: none; }
  .avatar-status[data-state="success"] { color: var(--accent); }
  .avatar-status[data-state="error"] { color: var(--danger); }
  .avatar-edit { cursor: pointer; display: block; margin: 0; position: relative; }
  .avatar-edit:hover .member-avatar-lg { filter: brightness(0.92); }
  .avatar-progress { background: conic-gradient(var(--accent) calc(var(--p, 0) * 1%), color-mix(in oklch, var(--accent) 20%, transparent) 0); border-radius: 50%; inset: 0; mask: radial-gradient(farthest-side, transparent calc(100% - 0.3rem), black calc(100% - 0.29rem)); pointer-events: none; position: absolute; }
  .avatar-progress[hidden] { display: none; }
  .token-meter-row { align-items: center; }
  .token-meter-row .settings-row-text { flex: 1; }
  .token-meter-value { display: flex; flex: none; flex-direction: column; font-variant-numeric: tabular-nums; text-align: right; }
  .token-meter-value small { color: var(--muted); font-size: 0.75rem; }
  .token-meter { accent-color: var(--accent); display: block; height: 0.45rem; margin-top: 0.4rem; width: 100%; }
  .avatar-edit-badge { align-items: center; background: var(--surface); border: 1px solid var(--line); border-radius: 50%; bottom: -0.15rem; box-shadow: 0 1px 3px rgb(0 0 0 / 0.15); color: var(--muted); display: flex; height: 1.5rem; justify-content: center; position: absolute; right: -0.15rem; width: 1.5rem; }
  .avatar-edit-badge svg { height: 0.9rem; width: 0.9rem; }
  .avatar-input { height: 1px; opacity: 0; position: absolute; width: 1px; }
  .avatar-clear { align-items: center; background: var(--surface); border: 1px solid var(--line); border-radius: 50%; box-shadow: 0 1px 3px rgb(0 0 0 / 0.15); color: var(--muted); cursor: pointer; display: flex; height: 1.4rem; justify-content: center; padding: 0; position: absolute; right: -0.15rem; top: -0.15rem; width: 1.4rem; z-index: 1; }
  .avatar-clear[hidden] { display: none; }
  .avatar-clear:hover { color: var(--danger); }
  .avatar-clear svg { height: 0.8rem; width: 0.8rem; }
  .member-avatar { align-items: center; background: var(--accent); border: 2px solid var(--paper); border-radius: 50%; color: white; display: inline-flex; font-size: 0.7rem; font-weight: 600; height: 1.6rem; justify-content: center; width: 1.6rem; }
  .member-avatar + .member-avatar { margin-left: -0.45rem; }

  .sheet-items form.button_to { display: block; }
  .sheet-item { background: transparent; border: 0; border-radius: 0.55rem; color: var(--ink); cursor: pointer; display: block; font-size: 1rem; padding: 0.7rem 0.65rem; text-align: left; width: 100%; }
  .sheet-item:hover, .sheet-item.active { background: color-mix(in oklch, var(--ink) 6%, transparent); text-decoration: none; }
  .sheet-item.active { font-weight: 600; }

  @media (max-width: 899.9px) {
    .page-shell { position: relative; transition: border-radius 240ms ease, transform 240ms ease; z-index: 1; }
    .page-shell { background: var(--paper); }
  }

  /* Settings is one object at every depth: a column of grouped rows on the
     paper. No cards inside cards, no floating buttons, and nothing that
     reads as a popup — the chrome carries the title and the way back. Every
     action either is a row or sits at the trailing edge of one, so the eye
     runs down a single left margin instead of hunting. */
  .settings-screen { display: grid; gap: 1.6rem; margin: 0 auto; max-width: 34rem; min-width: 0; }
  .settings-groups { display: grid; gap: 1.6rem; }

  /* Identity: who or what this screen is about, stated once and quietly. */
  .settings-identity { align-items: center; display: grid; gap: 0.85rem; justify-items: center; text-align: center; }
  .settings-identity-name { font-size: 1.35rem; font-weight: 700; letter-spacing: -0.02em; margin: 0; }
  .settings-identity-meta { color: var(--muted); font-size: 0.88rem; margin: 0; }

  .settings-section { display: grid; gap: 0.5rem; }
  .settings-group-title { color: var(--muted); font-size: 0.72rem; font-weight: 650; letter-spacing: 0.07em; margin: 0 0 0 0.9rem; text-transform: uppercase; }
  .settings-group { background: var(--surface); border: 1px solid var(--line); border-radius: 0.85rem; overflow: hidden; }
  .settings-group > * + * { border-top: 1px solid var(--line); }
  .settings-group form.button_to { display: block; }
  /* The iOS footnote: the sentence that explains a group without crowding it. */
  .settings-note { color: var(--muted); font-size: 0.82rem; line-height: 1.45; margin: 0 0.9rem; }

  .settings-row { align-items: center; background: transparent; border: 0; border-radius: 0; color: var(--ink); cursor: pointer; display: flex; font-size: 0.97rem; gap: 0.75rem; min-height: 3rem; padding: 0.7rem 0.9rem; text-align: left; width: 100%; }
  @media (hover: hover) and (pointer: fine) { .settings-row:hover { background: color-mix(in oklch, var(--ink) 3.5%, transparent); text-decoration: none; } }
  a.settings-row:not(.settings-row-action)::after { color: var(--muted); content: "›"; font-size: 1.15rem; margin-left: auto; padding-left: 0.5rem; }
  a.settings-row > .settings-row-text { flex: 1; }
  .settings-row-text { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }
  .settings-row-text small { color: var(--muted); font-size: 0.82rem; }
  .settings-row-text strong { font-weight: 620; overflow: hidden; text-overflow: ellipsis; }
  .settings-avatar { align-items: center; background: var(--accent); border-radius: 50%; color: white; display: flex; flex: 0 0 2.2rem; font-weight: 600; height: 2.2rem; justify-content: center; width: 2.2rem; }
  .settings-family-avatars { flex: none; }
  .settings-family-avatars .member-avatar { border-color: var(--surface); font-size: 0.8rem; height: 2rem; width: 2rem; }
  .settings-row-action { color: var(--c-accent, var(--accent)); font-weight: 560; }
  .settings-row-danger { color: var(--danger); }
  .settings-row-static { cursor: default; }
  .settings-row-static:hover { background: transparent; }

  /* Row actions: small, level with each other, always at the trailing edge.
     One weight for the affirmative, one for the destructive, nothing loud —
     a settings screen is not the place for a primary call to action. */
  .settings-row-trailing { display: flex; flex: none; gap: 0.4rem; margin-left: auto; padding-left: 0.5rem; }
  .settings-action { background: color-mix(in oklch, var(--ink) 6%, transparent); border: 0; border-radius: 0.5rem; color: var(--ink); cursor: pointer; font-size: 0.84rem; font-weight: 600; padding: 0.32rem 0.7rem; white-space: nowrap; }
  .settings-action:hover { background: color-mix(in oklch, var(--ink) 11%, transparent); text-decoration: none; }
  .settings-action.accent { background: var(--c-accent-soft, var(--accent-soft)); color: var(--c-accent, var(--accent)); }
  .settings-action.danger { background: color-mix(in oklch, var(--danger) 11%, transparent); color: var(--danger); }

  /* Forms live inside a group, one field per row, label above value. */
  .settings-field { display: grid; gap: 0.3rem; padding: 0.7rem 0.9rem; }
  .settings-field label { color: var(--muted); font-size: 0.76rem; font-weight: 650; letter-spacing: 0.05em; margin: 0; text-transform: uppercase; }
  .settings-field input, .settings-field select, .settings-field textarea { background: transparent; border: 0; border-radius: 0; padding: 0; }
  .settings-field input:focus, .settings-field select:focus, .settings-field textarea:focus { border: 0; outline: 2px solid color-mix(in oklch, var(--accent) 35%, transparent); outline-offset: 0.25rem; }
  .settings-field p { margin: 0; }
  .settings-submit { display: flex; justify-content: flex-end; }
  .form-error { color: var(--danger); }

  .settings-qr { background: white; border-radius: 0.6rem; margin: 0 auto; padding: 0.9rem; width: fit-content; }
  .settings-qr svg { height: auto; width: 11rem; }
  .settings-link { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.78rem; overflow-wrap: anywhere; }

  .rail { display: none; }
  .desktop-chat-placeholder { display: none; }
  .rail-nav { display: flex; flex-direction: column; gap: 0.125rem; margin-top: 0.9rem; }
  .rail-link { align-items: center; border-radius: 0.5rem; color: var(--ink); display: flex; font-size: 0.94rem; gap: 0.6rem; padding: 0.48rem 0.6rem; }
  .rail-link svg { height: 1.15rem; width: 1.15rem; opacity: 0.75; }
  .rail-link:hover { background: color-mix(in oklch, var(--ink) 5%, transparent); text-decoration: none; }
  .rail-link.active { background: color-mix(in oklch, var(--ink) 8%, transparent); font-weight: 600; }
  .rail-link.active svg { opacity: 1; }
  .rail-nav { flex: 1; min-height: 0; overflow-y: auto; }
  .rail-nav form.button_to { display: block; }
  .rail-new-chat { align-items: center; display: flex; gap: 0.4rem; }
  .rail-new-chat svg { flex: none; height: 1.1rem; width: 1.1rem; }
  .list-nav, .list-nav-section, .list-primary-links { display: flex; flex-direction: column; gap: 0.25rem; }
  .list-link { align-items: center; background: transparent; border: 0; border-radius: 0.5rem; color: var(--ink); display: flex; font-size: 0.97rem; gap: 0.4rem; padding: 0.55rem 0.65rem; }
  .list-link { -webkit-tap-highlight-color: transparent; }
  .list-link:hover { text-decoration: none; }
  @media (hover: hover) { .list-link:hover { background: color-mix(in oklch, var(--ink) 5%, transparent); } }
  .list-link:active { background: color-mix(in oklch, var(--ink) 8%, transparent); }
  .list-link .chat-link-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .list-link .member-avatar + .chat-link-title { margin-left: 0.1rem; }
  .list-section-label { color: var(--ink); font-size: 0.92rem; font-weight: 700; letter-spacing: 0; margin-top: 1.8rem; padding: 0 0.65rem 0.5rem; }
  .list-chat-row { align-items: center; gap: 0.6rem; }
  .chat-avatar { align-items: center; display: flex; flex: none; }
  .chat-avatar svg { height: 1.3rem; width: 1.3rem; }
  .list-chat-row .member-avatar { font-size: 1rem; height: 2.6rem; width: 2.6rem; }
  .recent-conversation-row .chat-link-title { flex: 1; min-width: 0; }
  .recent-conversation-tail { align-items: center; display: flex; flex: none; gap: 0.55rem; margin-left: auto; }
  .recent-conversation-tail .unread-dot { margin-left: 0; }
  .recent-conversation-avatars { align-items: center; display: flex; flex: none; }
  .recent-conversation-avatars .member-avatar { border-color: var(--surface); font-size: 0.72rem; height: 1.65rem; width: 1.65rem; }
  .chat-link-body { display: flex; flex: 1; flex-direction: column; gap: 0.1rem; min-width: 0; }
  .chat-link-top { align-items: center; display: flex; gap: 0.4rem; }
  .chat-link-top .chat-link-title { font-weight: 600; }
  .chat-link-meta { align-items: center; color: var(--muted); display: flex; flex: none; font-size: 0.82rem; gap: 0.1rem; margin-left: auto; }
  .chat-link-chevron { color: color-mix(in oklch, var(--muted) 55%, transparent); height: 0.85rem; width: 0.85rem; }
  .chat-link-preview { -webkit-box-orient: vertical; -webkit-line-clamp: 2; color: var(--muted); display: -webkit-box; font-size: 0.88rem; line-height: 1.3; overflow: hidden; }
  .chat-link-top .unread-dot { margin-left: 0; }
  .rail-chat-link { display: flex; align-items: center; gap: 0.4rem; }
  .rail-chat-link .chat-link-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .unread-dot { background: var(--accent); border-radius: 50%; flex: none; height: 0.5rem; margin-left: auto; width: 0.5rem; }
  .unread-dot[hidden] { display: none; }
  .history-sentinel { height: 1px; }
  .view-all-link { color: var(--muted); font-size: 0.9rem; }
  /* The welcome wears the launch face: the icon's gradient, so the splash,
     the sign-in and this screen never look like three different apps. The
     three stops are the ones SplashView.swift paints. */
  body.onboarding-page { --onboarding-chrome: #7a6bef; --onboarding-ink: #f4f1ff; background: linear-gradient(135deg, #8b7bff 0%, #6357dc 55%, #4032ac 100%) fixed; color: #f4f1ff; color-scheme: dark; }
  .signin { align-items: center; background: linear-gradient(135deg, #8b7bff 0%, #6357dc 55%, #4032ac 100%); color: #f4f1ff; color-scheme: dark; display: flex; inset: 0; justify-content: center; padding: max(1.5rem, env(safe-area-inset-top)) 1.25rem max(1.5rem, env(safe-area-inset-bottom)); position: fixed; }
  .signin-card { display: grid; justify-items: center; max-width: 22rem; text-align: center; width: 100%; }
  .signin-icon { border-radius: 1.4rem; box-shadow: 0 1rem 2.5rem rgb(20 10 80 / 0.35); height: 5.5rem; margin-bottom: 1.5rem; width: 5.5rem; }
  .signin h1 { font-family: Georgia, "Times New Roman", serif; font-size: 3rem; font-weight: 400; letter-spacing: -0.04em; margin: 0 0 0.5rem; }
  .signin p { font-size: 1.02rem; line-height: 1.5; margin: 0 0 2.25rem; opacity: 0.8; text-wrap: balance; }
  .signin-actions { display: grid; gap: 0.7rem; width: 100%; }
  .signin-actions form { margin: 0; }
  .signin-button { align-items: center; background: #fffaf1; border: 0; border-radius: 999px; color: #1f1a3d; cursor: pointer; display: flex; font-size: 1rem; font-weight: 650; gap: 0.65rem; justify-content: center; min-height: 3.1rem; transition: filter 150ms ease, transform 150ms ease; width: 100%; }
  .signin-button svg { height: 1.2rem; width: 1.2rem; }
  .signin-button:hover { filter: brightness(1.04); transform: translateY(-1px); }
  .signin-apple { background: #000; color: #fff; }
  .onboarding { min-height: 100svh; overflow: hidden; }
  .onboarding-frame { display: grid; grid-template-columns: minmax(22rem, 0.86fr) minmax(30rem, 1.14fr); min-height: 100svh; position: relative; }
  .onboarding-topline { align-items: center; display: flex; justify-content: space-between; left: 0; padding: max(1.35rem, env(safe-area-inset-top)) clamp(1.35rem, 4vw, 4rem); position: absolute; right: 0; top: 0; z-index: 5; }
  .onboarding-brand { align-items: center; display: flex; gap: 0.7rem; }
  .onboarding-brand img { border-radius: 0.65rem; height: 2.4rem; width: 2.4rem; }
  .onboarding-brand strong { font-size: 1.05rem; letter-spacing: -0.02em; }
  .onboarding-topline > span { font-size: 0.72rem; font-weight: 750; letter-spacing: 0.08em; opacity: 0.58; text-transform: uppercase; }
  .onboarding-copy { align-self: center; padding: 8rem 2rem 5rem clamp(2rem, 7vw, 7rem); position: relative; z-index: 4; }
  /* Grouping for the phone's benefit; on a wide screen they add no box. */
  .onboarding-body, .onboarding-action { display: contents; }
  .onboarding-eyebrow { color: #ffd9a8; font-size: 0.72rem; font-weight: 850; letter-spacing: 0.15em; margin: 0 0 1.5rem; text-transform: uppercase; }
  .onboarding-copy h1 { font-family: Georgia, "Times New Roman", serif; font-size: clamp(3.4rem, 6vw, 6.4rem); font-weight: 400; letter-spacing: -0.055em; line-height: 0.96; margin: 0 0 2rem; max-width: 9ch; }
  /* The second step carries the code as well as the words, so it gives back
     the room the first step spends on its headline. The padding is for the
     wide layout only: on a phone it would outrank the frame's own bottom
     padding and stand the button 20px higher than on the first step. */
  .onboarding-step-2 .onboarding-copy h1 { font-size: clamp(2.6rem, 4.2vw, 4.4rem); margin-bottom: 1.5rem; max-width: 14ch; }
  @media (min-width: 641px) {
    .onboarding-step-2 .onboarding-copy { padding-block: 6.5rem 2.5rem; }
  }
  .onboarding-intro { font-size: clamp(1rem, 1.5vw, 1.2rem); line-height: 1.65; margin: 0 0 2rem; max-width: 33rem; opacity: 0.77; }
  .onboarding-primary { align-items: center; background: #fffaf1; border: 0; border-radius: 999px; color: #3a2b8c; cursor: pointer; display: inline-flex; font-weight: 750; justify-content: center; min-height: 2.9rem; padding: 0.75rem 1.05rem 0.8rem 1.4rem; text-decoration: none; transition: filter 180ms ease, transform 180ms ease; }
  .onboarding-primary span { display: inline-block; margin-left: 1.2rem; transition: transform 180ms ease; }
  .onboarding-primary:hover { color: #3a2b8c; filter: brightness(1.05); text-decoration: none; transform: translateY(-2px); }
  .onboarding-primary:hover span { transform: translateX(0.25rem); }
  .onboarding-primary:focus-visible, .onboarding-invite button:focus-visible { outline: 3px solid rgb(255 255 255 / 0.45); outline-offset: 3px; }
  .onboarding-action small { display: block; margin-top: 1rem; max-width: 28rem; opacity: 0.58; }
  .onboarding-copy .button_to { display: inline; }
  /* The code to scan, and the link for everybody out of camera range. No card
     around them: the only white on the screen is the quiet zone the QR needs
     to be read at all. */
  .onboarding-invite { display: grid; gap: 0.85rem; justify-items: center; margin: 0 0 1.5rem; max-width: 30rem; width: 100%; }
  .onboarding-invite .onboarding-qr { margin: 0; padding: 0.55rem; width: min(9.5rem, 45vw); }
  .onboarding-invite button { background: #ece7ff; border: 0; border-radius: 0.65rem; color: #3a2b8c; cursor: pointer; flex: none; font-size: 0.84rem; font-weight: 750; min-height: 2.4rem; padding: 0.45rem 0.75rem; }
  .invite-link { font-size: 0.8rem; overflow-wrap: anywhere; }
  /* The code carries its own paper: dark modules need a light quiet zone to
     scan, whatever the page behind them is doing. */
  .invite-qr { background: #fff; border-radius: 0.6rem; margin: auto; padding: 0.75rem; width: min(16rem, 100%); }
  .invite-qr svg { display: block; width: 100%; }
  .onboarding-scene { min-height: 45rem; position: relative; }
  .onboarding-sun { background: radial-gradient(circle at 35% 35%, #ffe6b3, #ffc978); border-radius: 50%; height: min(35vw, 31rem); position: absolute; right: 10%; top: 9%; width: min(35vw, 31rem); }
  .onboarding-table { background: #3a2b8c; border-radius: 50% 0 0; bottom: -12%; height: 45%; position: absolute; right: -5%; transform: rotate(-4deg); width: 90%; }
  .onboarding-note { background: rgb(255 252 244 / 0.94); box-shadow: 0 1.2rem 3rem rgb(72 42 25 / 0.18); display: grid; padding: 1.3rem; position: absolute; width: min(18rem, 38vw); z-index: 2; }
  .onboarding-note { color: #3e3227; }
  .onboarding-note > span { color: #6357dc; font-size: 0.68rem; font-weight: 850; letter-spacing: 0.1em; text-transform: uppercase; }
  .onboarding-note > strong { font-family: Georgia, "Times New Roman", serif; font-size: 1.4rem; font-weight: 400; line-height: 1.2; margin-top: 1.2rem; }
  .onboarding-note > em { font-size: 0.74rem; font-style: normal; margin-top: 0.45rem; opacity: 0.55; }
  .onboarding-note-reminder { border-radius: 0.45rem 1.4rem 1.4rem; right: 31%; top: 25%; transform: rotate(-5deg); }
  .onboarding-note-message { border-radius: 1.4rem 1.4rem 0.45rem 1.4rem; right: 6%; top: 49%; transform: rotate(4deg); }
  .onboarding-note-family { border-radius: 0.45rem 1.8rem 1.8rem; right: 12%; top: 30%; transform: rotate(3deg); width: min(23rem, 50vw); }
  .onboarding-note-family > strong { font-size: 1.65rem; }
  .onboarding-avatars { display: flex; margin-top: 1.2rem; }
  .onboarding-avatars b { align-items: center; background: #e8cbb1; border: 2px solid #fffaf1; border-radius: 50%; display: flex; font-size: 0.58rem; height: 1.8rem; justify-content: center; margin-left: -0.35rem; width: 1.8rem; }
  .onboarding-avatars b:first-child { margin-left: 0; }
  .onboarding-vase { bottom: 12%; height: 10rem; position: absolute; right: 56%; width: 5rem; z-index: 2; }
  .onboarding-vase::after { background: #d6cab4; border-radius: 45% 45% 30% 30%; bottom: 0; content: ""; height: 5.8rem; left: 0.4rem; position: absolute; width: 4.2rem; }
  .onboarding-vase i { background: #496b49; bottom: 5rem; height: 6rem; left: 2.3rem; position: absolute; transform: rotate(-20deg); width: 2px; }
  .onboarding-vase i::before { background: #f5c8c7; border-radius: 50%; content: ""; height: 2rem; left: -1rem; position: absolute; top: -0.8rem; width: 2rem; }
  .onboarding-vase i:nth-child(2) { height: 7rem; transform: rotate(5deg); }
  .onboarding-vase i:nth-child(2)::before { background: #fff0bd; }
  .onboarding-vase i:nth-child(3) { height: 5.5rem; transform: rotate(25deg); }
  .onboarding-vase i:nth-child(3)::before { background: #c9c0e7; }

  @media (max-width: 800px) {
    .onboarding { overflow: visible; }
    .onboarding-frame { grid-template-columns: 1fr; }
    .onboarding-copy { align-self: start; padding: calc(max(6.8rem, env(safe-area-inset-top) + 5rem)) 1.25rem 2rem; }
    .onboarding-copy h1 { font-size: clamp(3.1rem, 13vw, 5rem); }
    .onboarding-step-2 .onboarding-copy h1 { font-size: clamp(2.75rem, 11vw, 4.5rem); }
    .onboarding-intro { max-width: 36rem; }
    .onboarding-scene { min-height: 27rem; }
    .onboarding-sun { height: 20rem; right: -2rem; top: 0; width: 20rem; }
    .onboarding-table { width: 105%; }
    .onboarding-note { width: min(16rem, 64vw); }
    .onboarding-note-reminder { right: 26%; top: 8%; }
    .onboarding-note-message { right: 2%; top: 42%; }
    .onboarding-note-family { right: 5%; top: 14%; width: min(21rem, 78vw); }
    .onboarding-vase { bottom: 4%; left: 8%; right: auto; }
  }

  /* One phone screen, no scrolling: the drawn scene goes, the daisy of the
     splash stands in its place, and every size that could push the button off
     the bottom is measured against the height of the screen rather than its
     width. */
  @media (max-width: 640px) {
    /* The topline floats and the scene is gone, so the copy is the only row
       there is — and it takes the whole screen to centre itself in. */
    .onboarding-frame { grid-template-rows: 1fr; height: 100svh; min-height: 0; }
    .onboarding-scene { display: none; }
    /* Words in the middle of the screen, button against the bottom of it. */
    .onboarding-copy { align-self: stretch; display: grid; grid-template-rows: 1fr auto; padding: calc(env(safe-area-inset-top) + 4.5rem) 1.25rem calc(env(safe-area-inset-bottom) + 1.25rem); }
    .onboarding-body { align-content: center; display: grid; justify-items: start; }
    .onboarding-action { align-self: end; display: grid; gap: 0.55rem; justify-items: start; padding-top: min(1.25rem, 2.2svh); }
    .onboarding-eyebrow { margin-bottom: min(1rem, 1.8svh); }
    .onboarding-copy h1 { font-size: min(2.9rem, 6.4svh); margin-bottom: min(1.25rem, 2.2svh); }
    .onboarding-step-2 .onboarding-copy h1 { font-size: min(2.4rem, 5.4svh); }
    .onboarding-intro { font-size: 0.98rem; line-height: 1.5; margin-bottom: min(1.5rem, 2.6svh); }
    .onboarding-invite { margin-bottom: 0; width: 100%; }
    .onboarding-invite .onboarding-qr { width: min(9rem, 20svh); }
    .onboarding-action small { margin-top: 0; }
  }

  @media (max-width: 460px) {
    .onboarding-topline { padding-inline: 1.15rem; }
    .onboarding-copy { padding-inline: 1.15rem; }
    .onboarding-note-reminder { right: 18%; }
  }

  @media (prefers-reduced-motion: no-preference) {
    .onboarding-copy > * { animation: onboarding-rise 520ms both; }
    .onboarding-copy > *:nth-child(2) { animation-delay: 60ms; }
    .onboarding-copy > *:nth-child(3) { animation-delay: 120ms; }
    .onboarding-copy > *:nth-child(4) { animation-delay: 180ms; }
    .onboarding-note-reminder { animation: onboarding-note-float 5s ease-in-out infinite; }
    .onboarding-note-message, .onboarding-note-family { animation: onboarding-note-float 5.5s 600ms ease-in-out infinite reverse; }
  }

  @keyframes onboarding-rise { from { opacity: 0; transform: translateY(1rem); } }
  @keyframes onboarding-note-float { 50% { margin-top: -0.6rem; } }
  .chat-shell { position: relative; }
  .rail-section-label { color: var(--ink); font-size: 0.84rem; font-weight: 700; letter-spacing: 0; margin-top: 1.4rem; padding: 0 0.6rem 0.4rem; }
  .recent-menu-section { display: flex; flex-direction: column; gap: 0.125rem; }
  .recent-menu-heading { align-items: center; display: flex; justify-content: space-between; margin-top: 1.4rem; padding: 0 0.6rem 0.4rem; }
  .recent-menu-heading .rail-section-label { margin: 0; padding: 0; }
  .recent-show-all { background: transparent; border: 0; color: var(--accent); cursor: pointer; font-size: 0.78rem; font-weight: 600; padding: 0; }
  .recent-show-all[hidden], .recent-conversation-row[hidden] { display: none; }
  .rail .recent-conversation-avatars .member-avatar { font-size: 0.62rem; height: 1.4rem; width: 1.4rem; }
  .member-avatar { align-items: center; background: var(--accent); border-radius: 50%; color: white; display: inline-flex; flex: none; font-size: 0.7rem; font-weight: 600; height: 1.45rem; justify-content: center; width: 1.45rem; }
  .rail-chat-link .member-avatar + .chat-link-title { margin-left: 0.1rem; }
  .rail-chat-link.active .unread-dot { display: none; }
  .rail-avatar { align-items: center; background: var(--accent); border-radius: 50%; color: white; display: flex; flex: 0 0 2rem; font-size: 0.9rem; font-weight: 600; height: 2rem; justify-content: center; width: 2rem; }

  @media (min-width: 900px) {
    /* Settings is a place you go, not a thing that opened over the app. The
       rail stays beside it and the topbar carries the way back, so nothing
       here floats, dims, or asks to be dismissed. */
    .settings-screen { padding-top: 0.5rem; }
    .rail { background: var(--paper); border-right: 1px solid var(--line); display: block; grid-row: 1 / -1; height: 100dvh; min-width: 0; overflow: hidden; position: sticky; top: 0; }
    .rail > .conversation-index, .conversation-master-layout .conversation-index { display: grid; grid-template-rows: 3.5rem minmax(0, 1fr); height: 100%; margin: 0; max-width: none; min-height: 0; overflow: hidden; padding: 0 1rem; }
    .rail .conversation-list, .conversation-master-layout .conversation-list { min-height: 0; overflow-y: auto; padding-bottom: 6.75rem; }
    .rail .conversation-new-form, .conversation-master-layout .conversation-new-form { bottom: 1.4rem; left: 50%; position: absolute; transform: translateX(-50%); z-index: 25; }
    .rail .conversation-new-button, .conversation-master-layout .conversation-new-button { bottom: auto; left: auto; position: static; transform: none; }
    .rail .conversation-new-button:active, .conversation-master-layout .conversation-new-button:active { transform: scale(0.94); }
    /* The rail drops the centering transform, so its lift has to stand alone. */
    @media (hover: hover) and (pointer: fine) {
      .rail .conversation-new-button:hover, .conversation-master-layout .conversation-new-button:hover { transform: translateY(-0.15rem); }
    }
    .rail .conversation-row.active { background: color-mix(in oklch, var(--ink) 6%, transparent); border-radius: 0.75rem; }
    .page-shell { display: grid; grid-template-columns: 24rem minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
    .topbar { grid-column: 2; grid-row: 1; }
    .topbar .menu-button { visibility: hidden; }
    /* A screen deeper than the rail can reach keeps its way back on desktop
       too — otherwise the only exit is the browser's own button. */
    .topbar .topbar-back-declared { visibility: visible; }
    .page-shell > .page-content { grid-column: 2; grid-row: 2; }
    .page-mobile-menu { display: none; }
    .app-chat-frame { display: grid; grid-template-columns: 24rem minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
    .app-chat-frame > .rail { grid-column: 1; grid-row: 1; height: 100%; min-height: 0; }
    .app-chat-frame > .chat-shell { grid-column: 2; grid-row: 1; min-height: 0; }
    .chat-header > .chat-menu-button { visibility: hidden; }
    .conversation-master-layout { grid-template-columns: 24rem minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); height: 100dvh; overflow: hidden; }
    .conversation-master-layout > .page-content { display: grid; grid-column: 1 / -1; grid-row: 1; grid-template-columns: 24rem minmax(0, 1fr); height: 100%; max-width: none; padding: 0; }
    .conversation-master-layout .conversation-index { border-right: 1px solid var(--line); grid-column: 1; }
    .desktop-chat-placeholder { align-items: center; color: var(--muted); display: flex; flex-direction: column; grid-column: 2; justify-content: center; min-width: 0; }
    .desktop-chat-placeholder img { height: 4.5rem; margin-bottom: 1.25rem; opacity: 0.16; width: 4.5rem; }
    .desktop-chat-placeholder strong { color: var(--ink); font-size: 1.05rem; font-weight: 600; }
    .desktop-chat-placeholder span { font-size: 0.88rem; margin-top: 0.2rem; }
  }
}

@layer chat {
  /* Chat theme contract: every colour inside the chat screen derives from
     these tokens, which default to the standard app palette. Themes change
     colours only — never typography. Each member picks one light and one
     dark theme; the shell carries both as classes and the device colour
     scheme selects the active palette. */
  /* Defaults live on :root so bubbles and proposal cards render correctly
     outside the chat shell too (approvals pages, previews). */
  :root {
    --c-bg: var(--surface);
    --c-ink: var(--ink);
    --c-muted: var(--muted);
    --c-line: var(--line);
    --c-accent: var(--accent);
    --c-accent-soft: var(--accent-soft);
    --c-member: var(--accent);
    --c-member-ink: white;
    --c-assistant: color-mix(in oklch, var(--ink) 6%, var(--surface));
    --c-assistant-ink: var(--ink);
    --c-own-bubble: var(--c-member);
    --c-own-bubble-ink: var(--c-member-ink);
    --c-family-bubble: var(--c-assistant);
    --c-family-bubble-ink: var(--c-assistant-ink);
    --c-agent-bubble: var(--c-accent-soft);
    --c-agent-bubble-ink: var(--c-ink);
    --c-field: color-mix(in oklch, var(--ink) 5%, var(--surface));
  }

  /* Three themes, each carrying its own light and dark and following the
     device, so there is one pick to make instead of a pairing and a switch.

     Elfmio is the house look: marigold on warm paper, the launch colours.
     iOS and Android are what those two systems look like, so somebody
     arriving from either keeps reading the chat the way they already do.

     .chat-theme-<name> is what the shell wears and it follows the device.
     .chat-theme-<name>-light / -dark pin one half unconditionally, which is
     how the picker shows both halves whatever the device is doing. */
  .chat-theme-elfmio-light { color-scheme: light; --c-bg: #ffffff; --c-ink: #352800; --c-muted: #8a7a4e; --c-line: #ece5d5; --c-accent: #a8760a; --c-accent-soft: #fdf0cc; --c-member: #f1c453; --c-member-ink: #352800; --c-assistant: #fff0b8; --c-assistant-ink: #352800; --c-own-bubble: #f1c453; --c-own-bubble-ink: #352800; --c-family-bubble: #f7dda0; --c-family-bubble-ink: #352800; --c-agent-bubble: #fff0b8; --c-agent-bubble-ink: #352800; --c-field: #f6f1e6; }
  .chat-theme-elfmio-dark { color-scheme: dark; --c-bg: #000000; --c-ink: #f6ecd2; --c-muted: #b3a276; --c-line: #2b2616; --c-accent: #f1c453; --c-accent-soft: #3a3113; --c-member: #f1c453; --c-member-ink: #241c00; --c-assistant: #3a3113; --c-assistant-ink: #f6ecd2; --c-own-bubble: #e0b23e; --c-own-bubble-ink: #241c00; --c-family-bubble: #4a3d16; --c-family-bubble-ink: #f6ecd2; --c-agent-bubble: #3a3113; --c-agent-bubble-ink: #f6ecd2; --c-field: #17140c; }
  .chat-theme-imessage-light { color-scheme: light; --c-bg: #ffffff; --c-ink: #000000; --c-muted: #8e8e93; --c-line: #e5e5ea; --c-accent: #007aff; --c-accent-soft: #e5f0ff; --c-member: #007aff; --c-member-ink: #ffffff; --c-assistant: #e9e9eb; --c-assistant-ink: #000000; --c-own-bubble: #007aff; --c-own-bubble-ink: #ffffff; --c-family-bubble: #e9e9eb; --c-family-bubble-ink: #000000; --c-agent-bubble: #e5f0ff; --c-agent-bubble-ink: #000000; --c-field: #f2f2f7; }
  .chat-theme-imessage-dark { color-scheme: dark; --c-bg: #000000; --c-ink: #ffffff; --c-muted: #8e8e93; --c-line: #2c2c2e; --c-accent: #0a84ff; --c-accent-soft: #0a2647; --c-member: #0a84ff; --c-member-ink: #ffffff; --c-assistant: #26252a; --c-assistant-ink: #ffffff; --c-own-bubble: #0a84ff; --c-own-bubble-ink: #ffffff; --c-family-bubble: #26252a; --c-family-bubble-ink: #ffffff; --c-agent-bubble: #0a2647; --c-agent-bubble-ink: #ffffff; --c-field: #1c1c1e; }
  .chat-theme-android-light { color-scheme: light; --c-bg: #ffffff; --c-ink: #1f1f1f; --c-muted: #5f6368; --c-line: #e3e3e3; --c-accent: #0b57d0; --c-accent-soft: #d3e3fd; --c-member: #0b57d0; --c-member-ink: #ffffff; --c-assistant: #e3ecfd; --c-assistant-ink: #1a1c1e; --c-own-bubble: #0b57d0; --c-own-bubble-ink: #ffffff; --c-family-bubble: #e3ecfd; --c-family-bubble-ink: #1a1c1e; --c-agent-bubble: #f0e7ff; --c-agent-bubble-ink: #241b35; --c-field: #eef2fb; }
  .chat-theme-android-dark { color-scheme: dark; --c-bg: #131314; --c-ink: #e3e3e3; --c-muted: #9aa0a6; --c-line: #2d2e30; --c-accent: #a8c7fa; --c-accent-soft: #0842a0; --c-member: #0842a0; --c-member-ink: #d3e3fd; --c-assistant: #1e2733; --c-assistant-ink: #e1e6ee; --c-own-bubble: #0842a0; --c-own-bubble-ink: #d3e3fd; --c-family-bubble: #1e2733; --c-family-bubble-ink: #e1e6ee; --c-agent-bubble: #332d41; --c-agent-bubble-ink: #eaddff; --c-field: #1a222c; }

  @media (prefers-color-scheme: light) {
    .chat-theme-elfmio { color-scheme: light; --c-bg: #ffffff; --c-ink: #352800; --c-muted: #8a7a4e; --c-line: #ece5d5; --c-accent: #a8760a; --c-accent-soft: #fdf0cc; --c-member: #f1c453; --c-member-ink: #352800; --c-assistant: #fff0b8; --c-assistant-ink: #352800; --c-own-bubble: #f1c453; --c-own-bubble-ink: #352800; --c-family-bubble: #f7dda0; --c-family-bubble-ink: #352800; --c-agent-bubble: #fff0b8; --c-agent-bubble-ink: #352800; --c-field: #f6f1e6; }
    .chat-theme-imessage { color-scheme: light; --c-bg: #ffffff; --c-ink: #000000; --c-muted: #8e8e93; --c-line: #e5e5ea; --c-accent: #007aff; --c-accent-soft: #e5f0ff; --c-member: #007aff; --c-member-ink: #ffffff; --c-assistant: #e9e9eb; --c-assistant-ink: #000000; --c-own-bubble: #007aff; --c-own-bubble-ink: #ffffff; --c-family-bubble: #e9e9eb; --c-family-bubble-ink: #000000; --c-agent-bubble: #e5f0ff; --c-agent-bubble-ink: #000000; --c-field: #f2f2f7; }
    .chat-theme-android { color-scheme: light; --c-bg: #ffffff; --c-ink: #1f1f1f; --c-muted: #5f6368; --c-line: #e3e3e3; --c-accent: #0b57d0; --c-accent-soft: #d3e3fd; --c-member: #0b57d0; --c-member-ink: #ffffff; --c-assistant: #e3ecfd; --c-assistant-ink: #1a1c1e; --c-own-bubble: #0b57d0; --c-own-bubble-ink: #ffffff; --c-family-bubble: #e3ecfd; --c-family-bubble-ink: #1a1c1e; --c-agent-bubble: #f0e7ff; --c-agent-bubble-ink: #241b35; --c-field: #eef2fb; }
  }
  @media (prefers-color-scheme: dark) {
    .chat-theme-elfmio { color-scheme: dark; --c-bg: #000000; --c-ink: #f6ecd2; --c-muted: #b3a276; --c-line: #2b2616; --c-accent: #f1c453; --c-accent-soft: #3a3113; --c-member: #f1c453; --c-member-ink: #241c00; --c-assistant: #3a3113; --c-assistant-ink: #f6ecd2; --c-own-bubble: #e0b23e; --c-own-bubble-ink: #241c00; --c-family-bubble: #4a3d16; --c-family-bubble-ink: #f6ecd2; --c-agent-bubble: #3a3113; --c-agent-bubble-ink: #f6ecd2; --c-field: #17140c; }
    .chat-theme-imessage { color-scheme: dark; --c-bg: #000000; --c-ink: #ffffff; --c-muted: #8e8e93; --c-line: #2c2c2e; --c-accent: #0a84ff; --c-accent-soft: #0a2647; --c-member: #0a84ff; --c-member-ink: #ffffff; --c-assistant: #26252a; --c-assistant-ink: #ffffff; --c-own-bubble: #0a84ff; --c-own-bubble-ink: #ffffff; --c-family-bubble: #26252a; --c-family-bubble-ink: #ffffff; --c-agent-bubble: #0a2647; --c-agent-bubble-ink: #ffffff; --c-field: #1c1c1e; }
    .chat-theme-android { color-scheme: dark; --c-bg: #131314; --c-ink: #e3e3e3; --c-muted: #9aa0a6; --c-line: #2d2e30; --c-accent: #a8c7fa; --c-accent-soft: #0842a0; --c-member: #0842a0; --c-member-ink: #d3e3fd; --c-assistant: #1e2733; --c-assistant-ink: #e1e6ee; --c-own-bubble: #0842a0; --c-own-bubble-ink: #d3e3fd; --c-family-bubble: #1e2733; --c-family-bubble-ink: #e1e6ee; --c-agent-bubble: #332d41; --c-agent-bubble-ink: #eaddff; --c-field: #1a222c; }
  }

  /* The picker's preview tiles: a tiny two-bubble chat rendered from the
     same tokens the real shell uses. */
  .theme-grid { display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); margin-top: 1rem; }
  .theme-tile { background: var(--surface); border: 1px solid var(--line); border-radius: 0.75rem; cursor: pointer; display: flex; flex-direction: column; overflow: hidden; padding: 0; text-align: left; }
  .theme-tile.selected { outline: 2px solid var(--accent); outline-offset: 2px; }
  .theme-tile-screen { background: var(--c-bg); display: flex; flex-direction: column; gap: 0.35rem; padding: 0.75rem; }
  .theme-tile-bubble { border-radius: 0.6rem; font-size: 0.8rem; padding: 0.25rem 0.55rem; width: fit-content; }
  .theme-tile-bubble.opponent { background: var(--c-family-bubble); border-bottom-left-radius: 0.2rem; color: var(--c-family-bubble-ink); }
  .theme-tile-bubble.member { align-self: flex-end; background: var(--c-own-bubble); border-bottom-right-radius: 0.2rem; color: var(--c-own-bubble-ink); }
  .theme-tile-name { border-top: 1px solid var(--line); color: var(--ink); font-size: 0.85rem; font-weight: 600; padding: 0.5rem 0.75rem; }
  /* One swatch three ways: the assistant's shapes, in whichever theme the
     page is wearing. The samples repeat the thread's own rules at tile size. */
  .bubble-tile-screen { justify-content: center; min-height: 3.5rem; }
  .bubble-tile-sample { color: var(--c-ink); font-size: 0.8rem; padding: 0.25rem 0.55rem; width: fit-content; }
  .bubble-sample-none { padding-left: 0; }
  .bubble-sample-filled { background: var(--c-agent-bubble); border-radius: 0.6rem 0.6rem 0.6rem 0.2rem; color: var(--c-agent-bubble-ink); }
  .bubble-sample-outlined { border: 1px solid color-mix(in oklch, var(--c-agent-bubble) 55%, var(--c-ink)); border-radius: 0.6rem 0.6rem 0.6rem 0.2rem; }
  /* The tile shows what banded is: the fill reaching both edges of the little
     screen, so it cancels the screen's own padding. */
  .bubble-sample-banded {
    background: color-mix(in oklch, var(--c-agent-bubble) 55%, var(--c-bg));
    color: var(--c-agent-bubble-ink);
    margin-inline: -0.75rem; padding-inline: 1.3rem; width: auto;
  }
  /* A theme is a pair, so the tile shows the pair — both halves at once,
     whatever the device happens to be doing. */
  .theme-tile-pair { display: grid; grid-template-columns: 1fr 1fr; }
  .theme-tile-paired { grid-column: span 2; }
  .theme-grid:has(.theme-tile-paired) { grid-template-columns: 1fr 1fr; }

  .app-chat-frame { background: var(--paper); height: 100dvh; overflow: hidden; overscroll-behavior: none; }
  /* One row height for the header, shared with the menu anchor that floats
     over it — the anchor is a sibling, so it cannot read this off the header
     itself. */
  .chat-shell { --chat-header-row: 2.75rem; display: flex; flex-direction: column; height: 100%; background: var(--c-bg); color: var(--c-ink); }
  .chat-shell a { color: var(--c-accent); }
  .chat-shell .button { background: var(--c-member); color: var(--c-member-ink); }
  .chat-shell .button.secondary { background: var(--c-accent-soft); color: var(--c-accent); }
  .chat-header { align-items: center; backdrop-filter: blur(16px); background: color-mix(in oklch, var(--c-bg) 86%, transparent); border-bottom: 1px solid var(--c-line); display: grid; grid-template-columns: 2.5rem 1fr var(--chat-header-row); min-height: 3.5rem; padding: max(0.45rem, env(safe-area-inset-top)) 0.65rem 0.45rem; text-align: center; z-index: 10; }
  .chat-header form { display: contents; }
  .chat-shell a.chat-heading { color: var(--c-ink); text-decoration: none; -webkit-tap-highlight-color: transparent; }
  .chat-heading:active .chat-heading-pill { background: color-mix(in oklch, var(--c-field) 90%, var(--c-ink) 10%); }
  .chat-heading-avatars .member-avatar-photo { object-fit: cover; }
  .chat-heading { align-items: center; display: flex; flex-direction: column; min-width: 0; padding-block: 0.3rem; }
  .chat-heading-avatars { display: flex; justify-content: center; margin-bottom: -0.4rem; position: relative; z-index: 1; }
  .chat-heading-avatars .member-avatar { border: 2px solid var(--c-bg); font-size: 1.1rem; height: 2.75rem; width: 2.75rem; }
  .chat-heading-avatars .member-avatar + .member-avatar { margin-left: -0.65rem; }
  .chat-heading-more { background: var(--c-field); color: var(--c-ink); }
  .chat-heading .chat-heading-pill { align-items: center; backdrop-filter: blur(16px); background: color-mix(in oklch, var(--c-field) 65%, transparent); border: 1px solid color-mix(in oklch, var(--c-ink) 8%, transparent); border-radius: 2rem; display: flex; font-size: 0.9rem; gap: 0.25rem; max-width: 100%; padding: 0.45rem 0.75rem 0.45rem 1rem; }
  .chat-heading-avatars + .chat-heading-pill { padding-top: 0.75rem; }
  .chat-heading-chevron { color: color-mix(in oklch, var(--c-ink) 45%, transparent); flex: none; }
  .chat-menu-button { align-items: center; background: transparent; border: 0; border-radius: 50%; color: var(--c-ink); display: flex; height: 2.5rem; justify-content: center; width: 2.5rem; }

  /* The + popup rises from the + beside the field and holds what can be
     added. Each row is the system picker wearing a row, so a tap opens the
     picker and nothing else (issue #115). Camera is for touch screens. */
  .composer-add { align-self: flex-end; flex: none; position: relative; }
  .chat-add-scrim { display: none; inset: 0; position: fixed; z-index: 40; }
  .chat-add-menu { animation: menu-pop 140ms ease both; background: color-mix(in oklch, var(--c-bg) 92%, var(--c-ink) 8%); border: 1px solid color-mix(in oklch, var(--c-ink) 8%, transparent); border-radius: 0.9rem; bottom: calc(100% + 0.6rem); box-shadow: 0 10px 34px rgb(0 0 0 / 0.16); display: none; left: 0; padding: 0.3rem; position: absolute; transform-origin: bottom left; width: 14rem; z-index: 41; }
  body.chat-add-open .chat-add-menu, body.chat-add-open .chat-add-scrim { display: block; }
  .chat-add-menu label.chat-menu-row { cursor: pointer; font-weight: inherit; margin: 0; }
  .chat-add-file-input { height: 1px; opacity: 0; position: absolute; width: 1px; }
  @media (hover: hover) and (pointer: fine) { .chat-add-camera { display: none; } }
  .chat-menu-row { align-items: center; background: transparent; border: 0; border-radius: 0.6rem; color: var(--c-ink); cursor: pointer; display: flex; font-size: 0.97rem; gap: 0.7rem; padding: 0.65rem 0.7rem; text-align: left; width: 100%; }
  @media (hover: hover) and (pointer: fine) { .chat-menu-row:hover { background: color-mix(in oklch, var(--c-ink) 8%, transparent); text-decoration: none; } }
  .chat-menu-row svg { flex: 0 0 1.1rem; height: 1.1rem; width: 1.1rem; }

  /* The theme popup: a framed, labelled preview pane on top, the mode
     switch (auto follows the device, light/dark pin one pick), the two
     tile grids, and Apply. Tiles only repaint the preview; Apply persists
     and recolours the real shell. The popup's own chrome uses the app
     tokens so it matches the rest of the UI — only the preview pane is
     themed. Padding doubles as room for the selected tile's outline
     inside the scrollable box. */
  /* The preview and actions stay visible while only the tile grids scroll,
     so users can always cancel or apply their selection. */
  .chat-theme-sticky { background: var(--surface); flex: 0 0 auto; }
  .theme-preview-frame { background: var(--surface); border: 1.5px dashed color-mix(in oklch, var(--muted) 55%, transparent); border-radius: 0.9rem; padding: 0.45rem; }
  .theme-preview { background: var(--c-bg); border-radius: 0.6rem; color: var(--c-ink); overflow: hidden; }
  .theme-preview-thread { display: flex; flex-direction: column; gap: 0.4rem; padding: 0.75rem; }
  .theme-preview-thread .msg-row { margin: 0; }
  .theme-preview .bubble { font-size: 0.85rem; padding: 0.4rem 0.7rem; }
  .theme-preview .assistant-output { font-size: 0.85rem; padding-block: 0.4rem; }
  .theme-preview-composer { align-items: center; border-top: 1px solid var(--c-line); display: flex; gap: 0.45rem; padding: 0.5rem 0.75rem; }
  .theme-preview-field { background: var(--c-field); border-radius: 1rem; color: var(--c-muted); flex: 1; font-size: 0.85rem; padding: 0.3rem 0.7rem; }
  .theme-preview-mic { background: var(--c-member); border-radius: 50%; flex: 0 0 1.6rem; height: 1.6rem; }
  /* Popup buttons keep the app's own accent — the shell-level theme button
     override must not leak into the popup chrome. */
  /* The theme screen scrolls with the page: the preview and the modes stay
     in view while the tiles scroll past them. */
  .chat-theme-page { gap: 1rem; max-width: 28rem; }
  .chat-theme-form { display: grid; gap: 0.5rem; }
  .chat-theme-sticky { background: var(--paper); padding-bottom: 0.5rem; position: sticky; top: 0; z-index: 1; }
  .chat-theme-actions { display: flex; margin-top: 0.5rem; }
  .chat-theme-actions .button { flex: 1; }
  .chat-theme-options { padding: 0 0.15rem 0.15rem; }
  .chat-theme-page .theme-grid { gap: 0.5rem; grid-template-columns: 1fr 1fr; margin-top: 0; }
  .chat-theme-page .theme-grid.bubble-grid { grid-template-columns: repeat(3, 1fr); }
  .chat-theme-modes { background: color-mix(in oklch, var(--ink) 7%, transparent); border-radius: 0.6rem; display: flex; gap: 0.2rem; margin-top: 0.85rem; padding: 0.2rem; }
  .chat-theme-mode { background: transparent; border: 0; border-radius: 0.45rem; color: var(--ink); cursor: pointer; flex: 1; font-size: 0.85rem; font-weight: 600; padding: 0.35rem 0; }
  .chat-theme-mode.selected { background: var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / 0.15); }
  .chat-theme-menu-label { color: var(--muted); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.04em; margin: 0.8rem 0 0.35rem 0.1rem; text-transform: uppercase; }
  .theme-preview-frame .chat-theme-menu-label { margin: 0 0 0.3rem 0.3rem; }
  /* overflow-anchor: none — loadEarlier compensates its own prepend by
     scrollTop arithmetic; WebKit's native scroll anchoring (new in iOS 26)
     compensated it a second time, and the double correction leapt the
     reader down the thread on every history load. All anchoring in this
     scroller is manual. */
  .chat-thread { --chat-thread-inline: max(0.75rem, env(safe-area-inset-left)); flex: 1; overflow-anchor: none; overflow-y: auto; overscroll-behavior: contain; padding: 0.8rem var(--chat-thread-inline); }
  .welcome-mic { align-items: center; background: var(--c-member); border: 0; border-radius: 50%; box-shadow: 0 6px 20px color-mix(in oklch, var(--c-member) 35%, transparent); color: var(--c-member-ink); display: inline-flex; height: 4.5rem; justify-content: center; width: 4.5rem; }
  .welcome-mic svg { height: 2.1rem; width: 2.1rem; }
  .welcome-mic.recording { background: var(--danger); box-shadow: 0 0 0 0.55rem color-mix(in oklch, var(--danger) 20%, transparent); color: white; }
  .msg-row { display: flex; margin-top: 1.25rem; transition: opacity 400ms ease; }
  /* Older messages fade into history; the newest stay crisp. Recomputed
     automatically as streamed appends arrive. */
  .chat-thread .msg-row:nth-last-child(n+14) { opacity: 0.85; }
  .chat-thread .msg-row:nth-last-child(n+26) { opacity: 0.68; }
  .msg-row, .composer-form { margin-inline: auto; max-width: 42rem; }
  .msg-row-own { justify-content: flex-end; }
  .room-message-avatar { align-items: center; align-self: flex-end; background: var(--c-member); border: 2px solid var(--c-bg); border-radius: 50%; color: var(--c-member-ink); display: inline-flex; flex: 0 0 2rem; font-size: 0.75rem; font-weight: 600; height: 2rem; justify-content: center; object-fit: cover; width: 2rem; }
  .room-member-avatar { margin: 0 0.4rem 0.05rem 0; }
  .room-agent-avatar { margin: 0 0.4rem 0.05rem 0; }
  .msg-author { display: block; font-size: 0.72rem; margin-bottom: 0.15rem; opacity: 0.72; }
  .msg-edited { display: block; font-size: 0.65rem; margin-top: 0.15rem; opacity: 0.65; }
  .reply-source { display: block; font-size: 0.7rem; margin-bottom: 0.35rem; opacity: 0.72; }
  .extraction-columns { display: grid; gap: 1rem; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .extraction-columns h3 { font-size: 0.85rem; }
  .extraction-columns small { color: var(--muted); display: block; margin-top: 0.25rem; }
  .source-quote { font-style: italic; }
  @media (max-width: 700px) { .extraction-columns { grid-template-columns: 1fr; } }
  .msg-row[data-role="assistant"] { justify-content: flex-start; }
  .msg-row[data-role="assistant"] + .msg-row[data-role="user"]:not(.msg-row-own) { margin-top: 2.2rem; }
  .msg-row.msg-row-same-sender { margin-top: 0.35rem; }
  .msg-time { align-self: flex-end; color: var(--c-muted); font-size: 0.68rem; margin: 0 0.45rem 0.1rem; opacity: 0.8; }
  .msg-row-own .msg-time { order: -1; }
  .day-separator { color: var(--c-muted); font-size: 0.75rem; margin: 1rem auto 0.2rem; max-width: 42rem; text-align: center; }
  .bubble { border-radius: 1.25rem; font-size: 1rem; line-height: 1.42; max-width: 76%; overflow-wrap: anywhere; padding: 0.5rem 0.85rem; white-space: normal; }
  .msg-body { display: block; white-space: pre-wrap; }
  /* The card of a linked page, under the words, in the bubble's own tones. */
  .link-preview { background: rgb(0 0 0 / 6%); border-radius: 0.9rem; color: inherit; display: flex; flex-direction: column; margin-top: 0.5rem; max-width: 20rem; overflow: hidden; text-decoration: none; }
  .link-preview-image { aspect-ratio: 1.91; display: block; object-fit: cover; width: 100%; }
  .link-preview-text { display: flex; flex-direction: column; gap: 0.15rem; padding: 0.5rem 0.7rem 0.6rem; }
  .link-preview-title { font-size: 0.92rem; line-height: 1.3; }
  .link-preview-description { -webkit-box-orient: vertical; -webkit-line-clamp: 2; display: -webkit-box; font-size: 0.82rem; line-height: 1.3; opacity: 0.8; overflow: hidden; }
  .link-preview-site { font-size: 0.75rem; opacity: 0.65; text-transform: lowercase; }
  .bubble-member { background: var(--c-family-bubble); border-bottom-left-radius: 0.35rem; color: var(--c-family-bubble-ink); }
  .msg-row-own .bubble-member { background: var(--c-own-bubble); border-bottom-left-radius: 1.25rem; border-bottom-right-radius: 0.35rem; color: var(--c-own-bubble-ink); }
  .assistant-output { color: var(--c-ink); flex: 0 1 auto; min-width: 0; overflow-wrap: anywhere; padding-block: 0.5rem; white-space: normal; }
  .assistant-output-text { line-height: 1.42; }
  /* The assistant's messages can wear a bubble too. Both shapes share the
     member bubble's geometry and take their colour from the theme's agent
     tokens, so a pick is a shape and never a palette. */
  :is(.assistant-bubble-filled, .assistant-bubble-outlined) .assistant-output {
    border-radius: 1.25rem 1.25rem 1.25rem 0.35rem; max-width: 76%; padding: 0.5rem 0.85rem;
  }
  .assistant-bubble-filled .assistant-output { background: var(--c-agent-bubble); color: var(--c-agent-bubble-ink); }
  /* The fill read as a bubble on its own; a hairline of it does not, so the
     outline is the same tone taken far enough towards the ink to be seen. */
  .assistant-bubble-outlined .assistant-output { background: var(--c-bg); border: 1px solid color-mix(in oklch, var(--c-agent-bubble) 55%, var(--c-ink)); }
  /* Banded keeps the theme's agent fill and drops the bubble entirely: square,
     and running the full width of the thread, so a long answer reads as a
     passage rather than as something said. It cancels the thread's own inline
     padding to reach the edges and pads its text back by the same amount, so
     the words stay in line with every other bubble. On a wide window the
     thread is a centred column, so the band spans that column rather than the
     whole window. */
  .assistant-bubble-banded .assistant-output {
    background: color-mix(in oklch, var(--c-agent-bubble) 55%, var(--c-bg));
    color: var(--c-agent-bubble-ink); flex: 1 1 auto;
    margin-inline: calc(-1 * var(--chat-thread-inline, 0px));
    padding: 0.5rem var(--chat-thread-inline, 0px);
  }
  /* Reactions: one quiet pill hanging off the bubble's lower corner, the
     first three kinds and the total. The pill opens the message menu. */
  .msg-row { position: relative; }
  .msg-row:has(.reaction-pill) { margin-bottom: 0.95rem; }
  .bubble-reactions { bottom: -0.95rem; display: flex; left: 0.6rem; position: absolute; z-index: 2; }
  .msg-row:has(.room-message-avatar) .bubble-reactions { left: 3rem; }
  .msg-row-own .bubble-reactions { left: auto; right: 0.6rem; }
  .assistant-output { position: relative; }
  .assistant-output > .bubble-reactions { left: 0.2rem; }
  :is(.assistant-bubble-filled, .assistant-bubble-outlined) .assistant-output > .bubble-reactions { left: 0.6rem; }
  .msg-row[data-role="assistant"]:has(.reaction-pill) { margin-bottom: 0; }
  .assistant-output:has(.reaction-pill) { margin-bottom: 0.95rem; }
  .reaction-pill { align-items: center; background: var(--c-bg); border: 1px solid var(--c-line); border-radius: 999px; box-shadow: 0 1px 3px rgb(0 0 0 / 0.1); color: var(--c-muted); cursor: pointer; display: inline-flex; font-size: 0.95rem; gap: 0.05rem; height: 1.6rem; line-height: 1; padding: 0 0.42rem; }
  .reaction-pill-emoji { font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; }
  .reaction-pill-count { font-size: 0.72rem; font-variant-numeric: tabular-nums; font-weight: 650; margin-left: 0.2rem; }
  .reaction-pill.reaction-own { background: color-mix(in oklch, var(--c-accent) 12%, var(--c-bg)); border-color: color-mix(in oklch, var(--c-accent) 35%, var(--c-line)); color: var(--c-accent); }
  @media (hover: hover) and (pointer: fine) { .reaction-pill:hover { border-color: color-mix(in oklch, var(--c-ink) 22%, var(--c-line)); } }

  /* Records an answer made or showed, under the answer (elfmio-36o4): one
     scale for every card, as iOS draws them (elfmio-wkt2). */
  .msg-row:has(.record-cards) { flex-wrap: wrap; }
  .record-cards { display: grid; flex-basis: 100%; gap: 0.5rem; justify-items: start; margin-top: 0.4rem; }
  .msg-row:has(.room-message-avatar) .record-cards { padding-left: 2.4rem; }
  .record-card { background: var(--c-field); border-radius: 1rem; color: var(--c-ink); display: grid; max-width: min(21.25rem, 100%); padding: 0.75rem 0.9rem 0; width: 100%; }
  .record-card-kind { align-items: center; color: var(--c-muted); display: flex; font-size: 0.8rem; gap: 0.5rem; justify-content: space-between; margin-bottom: 0.3rem; }
  .record-card-kind span:first-child { align-items: center; display: flex; font-weight: 650; gap: 0.3rem; }
  .record-card-kind svg { height: 0.9rem; width: 0.9rem; }
  .record-card-title { font-size: 1.05rem; font-weight: 650; line-height: 1.3; }
  .record-card p { margin: 0; }
  .record-card-when { font-variant-numeric: tabular-nums; margin-top: 0.2rem !important; }
  .record-card-cancelled { color: var(--c-muted); text-decoration: line-through; }
  .record-card-note { color: var(--c-muted); font-size: 0.92rem; }
  .record-card-body { margin-top: 0.25rem !important; white-space: pre-line; }
  .record-card > p:last-of-type, .record-card-items, .record-card > form:not(:last-child) { margin-bottom: 0.55rem !important; }
  .record-card-items { display: grid; list-style: none; margin: 0.15rem 0 0; padding: 0; }
  .record-card form { margin: 0; }
  .record-card-tick { align-items: center; background: transparent; border: 0; color: var(--c-ink); cursor: pointer; display: flex; font-size: 1rem; gap: 0.75rem; margin-inline: -0.9rem; min-height: 2.75rem; padding: 0 0.9rem; text-align: left; width: calc(100% + 1.8rem); }
  @media (hover: hover) and (pointer: fine) { .record-card-tick:hover { background: color-mix(in oklch, var(--c-ink) 5%, transparent); } }
  .record-card-box { align-items: center; border: 2px solid color-mix(in oklch, var(--c-muted) 70%, transparent); border-radius: 0.4rem; color: transparent; display: flex; flex: none; height: 1.4rem; justify-content: center; width: 1.4rem; }
  .record-card-box svg { height: 0.95rem; width: 0.95rem; }
  .record-card-tick.done .record-card-box { background: var(--c-accent); border-color: var(--c-accent); color: var(--c-bg); }
  .record-card-tick.done .record-card-label { color: var(--c-muted); text-decoration: line-through; }
  .record-card-tick:active .record-card-box { transform: scale(0.9); }
  .record-card-more { color: var(--c-muted); font-size: 0.92rem; min-height: 2.75rem; padding-left: 2.15rem; display: flex; align-items: center; }
  .record-card-delete { align-items: center; background: transparent; border: 0; border-top: 1px solid var(--c-line); color: var(--danger); cursor: pointer; display: flex; font-size: 0.94rem; font-weight: 650; gap: 0.4rem; margin-inline: -0.9rem; min-height: 2.75rem; padding: 0 0.9rem; width: calc(100% + 1.8rem); }
  .record-card-delete svg { height: 1rem; width: 1rem; }
  @media (hover: hover) and (pointer: fine) { .record-card-delete:hover { background: color-mix(in oklch, var(--danger) 7%, transparent); } }

  /* The hover button beside a bubble. Present for the keyboard everywhere,
     visible only on hover or focus where there is a pointer; a touch screen
     long-presses the bubble instead. */
  .msg-menu-button { align-items: center; align-self: center; background: transparent; border: 0; border-radius: 50%; color: var(--c-muted); cursor: pointer; display: flex; flex: none; height: 2rem; justify-content: center; opacity: 0; padding: 0; transition: opacity 120ms ease, background-color 120ms ease; width: 2rem; }
  .msg-menu-button svg { height: 1.2rem; width: 1.2rem; }
  .msg-row-own .msg-menu-button { order: -2; }
  .msg-row:hover .msg-menu-button, .msg-menu-button:focus-visible, .msg-row-menu .msg-menu-button { opacity: 1; }
  .msg-menu-button:hover, .msg-row-menu .msg-menu-button { background: color-mix(in oklch, var(--c-ink) 7%, transparent); color: var(--c-ink); }
  @media not ((hover: hover) and (pointer: fine)) {
    .msg-menu-button { height: 1px; margin: 0; opacity: 0; overflow: hidden; position: absolute; width: 1px; }
    /* A long press opens the menu, so it must not also start a selection or
       the system callout. Copy is in the menu. */
    .chat-thread :is(.bubble, .assistant-output) { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
  }

  .message-menu-scrim { inset: 0; position: fixed; z-index: 60; }
  .message-menu-scrim-dim { -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); background: color-mix(in oklch, var(--c-bg) 45%, rgb(0 0 0 / 0.25)); animation: menu-fade 160ms ease both; }
  .msg-row-lifted { opacity: 1 !important; z-index: 61; }
  .msg-row-lifted :is(.bubble, .assistant-output) { animation: bubble-lift 180ms ease both; }
  .message-menu { animation: menu-pop 150ms ease both; display: grid; gap: 0.4rem; left: 0; position: fixed; top: 0; width: min(17.5rem, calc(100vw - 1rem)); z-index: 62; }
  .message-menu[hidden] { display: none; }
  .message-menu-reactions, .message-menu-picker, .message-menu-actions, .message-menu-reactors {
    background: color-mix(in oklch, var(--c-bg) 92%, var(--c-ink) 8%); border: 1px solid color-mix(in oklch, var(--c-ink) 8%, transparent);
    box-shadow: 0 10px 34px rgb(0 0 0 / 0.16), 0 1px 3px rgb(0 0 0 / 0.08); color: var(--c-ink);
  }
  .message-menu-reactions { align-items: center; border-radius: 999px; display: flex; justify-content: space-between; padding: 0.25rem; }
  .message-menu-reactions[hidden], .message-menu-picker[hidden], .message-menu-reactors[hidden] { display: none; }
  .message-menu-emoji { align-items: center; background: transparent; border: 0; border-radius: 50%; cursor: pointer; display: flex; font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; font-size: 1.45rem; height: 2.3rem; justify-content: center; padding: 0; transition: transform 120ms ease, background-color 120ms ease; width: 2.3rem; }
  @media (hover: hover) and (pointer: fine) { .message-menu-emoji:hover { background: color-mix(in oklch, var(--c-ink) 8%, transparent); transform: scale(1.12); } }
  .message-menu-emoji:active { transform: scale(0.92); }
  .message-menu-emoji.selected { background: color-mix(in oklch, var(--c-accent) 22%, transparent); }
  .message-menu-more { background: color-mix(in oklch, var(--c-ink) 7%, transparent); color: var(--c-muted); }
  .message-menu-more svg { height: 1.15rem; width: 1.15rem; }
  .message-menu-more[aria-expanded="true"] svg { transform: rotate(45deg); }
  .message-menu-picker { border-radius: 1.1rem; display: grid; gap: 0.1rem; grid-template-columns: repeat(auto-fill, minmax(2.3rem, 1fr)); max-height: 13.5rem; overflow-y: auto; overscroll-behavior: contain; padding: 0.4rem; }
  .message-menu-reactors { border-radius: 0.9rem; display: grid; font-size: 0.82rem; gap: 0.2rem; list-style: none; margin: 0; padding: 0.5rem 0.75rem; }
  .message-menu-reactors li { align-items: center; color: var(--c-muted); display: flex; gap: 0.45rem; }
  .message-menu-reactors li span { font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; font-size: 1rem; }
  .message-menu-actions { border-radius: 0.9rem; display: grid; overflow: hidden; }
  .message-menu-action { align-items: center; background: transparent; border: 0; color: var(--c-ink); cursor: pointer; display: flex; font-size: 0.97rem; justify-content: space-between; min-height: 2.75rem; padding: 0 0.95rem; text-align: left; width: 100%; }
  .message-menu-action[hidden] { display: none; }
  .message-menu-action:not([hidden]) ~ .message-menu-action:not([hidden]) { border-top: 1px solid color-mix(in oklch, var(--c-ink) 8%, transparent); }
  .message-menu-action svg { flex: none; height: 1.15rem; width: 1.15rem; }
  @media (hover: hover) and (pointer: fine) { .message-menu-action:hover { background: color-mix(in oklch, var(--c-ink) 6%, transparent); } }
  .message-menu-action:active { background: color-mix(in oklch, var(--c-ink) 10%, transparent); }
  .message-menu-danger { color: var(--danger); }
  .message-menu-actions:not(:has(.message-menu-action:not([hidden]))) { display: none; }
  .message-menu-touch { width: min(15.5rem, calc(100vw - 1rem)); }
  @keyframes menu-pop { from { opacity: 0; transform: scale(0.96); } }
  @keyframes menu-fade { from { opacity: 0; } }
  @keyframes bubble-lift { to { transform: scale(1.03); } }
  @media (prefers-reduced-motion: reduce) { .message-menu, .message-menu-scrim-dim, .msg-row-lifted :is(.bubble, .assistant-output) { animation: none; } }
  .location-request { display: grid; gap: 0.2rem; }
  .location-request span { color: var(--c-muted); font-size: 0.82rem; }
  .location-line { align-items: center; color: var(--c-muted); display: flex; font-size: 0.8rem; gap: 0.3rem; }
  .location-line svg { height: 0.9rem; width: 0.9rem; }
  /* The bar is padded for the mic's full halo (0.7rem taller than the
     pill on each side), while the input pill itself stays slim. */
  /* The composer floats over the thread rather than sitting under it, so
     the newest message keeps its full width of screen and slides beneath
     instead of stopping at a hard edge. No blur — like iMessage, a veil of
     the background colour lightens the bubbles as they slide under. The
     veil starts only at the pill's top edge (2.6rem, the top padding) and
     is full by the pill's bottom, so bubbles stay crisp until they are
     actually under the input (elfmio-orfh). The keyboard inset moves the
     same distance it always did — it just moves an offset now instead of
     a margin. */
  .chat-composer {
    background: linear-gradient(to bottom, transparent 2.6rem, color-mix(in oklch, var(--c-bg) 88%, transparent) 5.2rem);
    bottom: var(--keyboard-inset, 0px);
    left: 0; right: 0;
    padding: 2.6rem 0.65rem max(1.1rem, env(safe-area-inset-bottom));
    position: absolute;
    z-index: 10;
  }
  /* With the keyboard away the pill rests translucent over the thread,
     iMessage-style; focusing it (the keyboard rising) makes it solid. */
  .chat-composer:not(:focus-within) .composer-field { background: color-mix(in oklch, var(--c-field) 60%, transparent); }
  /* Room for the composer to stand over, so the last message still scrolls
     clear of it. The composer grows by the bottom safe area on devices with
     a home indicator, so the reserve grows with it. */
  .chat-thread { padding-bottom: var(--composer-height, calc(6rem + env(safe-area-inset-bottom))); }
  /* A new chat leads with one big microphone and a small type button. The
     chat leaves .chat-unstarted on the first word, spoken or typed, and the
     ordinary composer takes over. */
  .composer-launch { display: none; }
  /* The microphone keeps the middle of the bar whatever sits beside it, so
     the two side columns share the rest and the type button ends at the
     right edge, where the send button would be. */
  .chat-unstarted .composer-launch { align-items: center; display: grid; grid-template-columns: 1fr auto 1fr; margin-inline: auto; max-width: 42rem; }
  .chat-unstarted .composer-launch .welcome-mic { grid-column: 2; }
  .chat-unstarted .composer-launch .composer-type { grid-column: 3; justify-self: start; margin-left: 1rem; }
  .welcome-mic-hint { color: var(--c-muted); font-size: 0.85rem; grid-column: 1 / -1; margin-top: 0.7rem; text-align: center; }
  .chat-unstarted .composer-form { display: none; }
  .composer-type { align-items: center; background: var(--c-field); border: 0; border-radius: 50%; color: var(--c-muted); display: flex; height: 3.25rem; justify-content: center; padding: 0; width: 3.25rem; }
  .composer-type svg { height: 1.65rem; width: 1.65rem; }
  .composer-form { align-items: flex-end; display: flex; gap: 0.5rem; }
  .composer-input { background: var(--c-field); border-radius: 0.7rem; color: var(--c-ink); font-size: 1rem; line-height: 1.35; max-height: 7.5rem; min-height: 2.5rem; padding: 0.575rem 0.85rem; resize: none; }
  .composer-input::placeholder { color: var(--c-muted); }
  /* One rounded pill holds the text field and the mic; the send circle stays
     outside. While recording, the input fades out (keeping its height, so
     the composer never moves) and the live waveform draws over it. */
  .composer-field { flex-direction: column; align-items: stretch; background: var(--c-field); border-radius: 1.25rem; display: flex; flex: 1; min-width: 0; position: relative; transition: background-color 150ms ease; }
  .composer-text-row { align-items: flex-end; display: flex; min-width: 0; }
  .composer-attachments { display: flex; gap: 0.6rem; overflow-x: auto; padding: 0.65rem; }
  .composer-attachments[hidden] { display: none; }
  .composer-attachment { align-items: center; border-radius: 0.75rem; display: flex; flex: 0 0 5rem; height: 5rem; justify-content: center; overflow: hidden; position: relative; }
  .composer-attachment img, .composer-attachment video { height: 100%; object-fit: cover; position: absolute; width: 100%; }
  .composer-attachment span { font-size: 0.7rem; overflow-wrap: anywhere; padding: 0.4rem; }
  .composer-attachment button { background: rgb(0 0 0 / 65%); border: 0; border-radius: 50%; color: white; height: 1.5rem; line-height: 1; padding: 0; position: absolute; right: 0.2rem; top: 0.2rem; width: 1.5rem; z-index: 1; }
  .attachment-status { color: var(--c-muted); display: block; font-size: 0.8rem; margin-inline: auto; max-width: 42rem; }
  .composer-field .composer-input { background: transparent; border: 0; border-radius: 1.25rem; flex: 1; min-width: 0; }
  .composer-field .composer-input:focus { border: 0; outline: none; }
  /* The voice action lives inside the field's pill, as on iOS
     (elfmio-vtvb): an ink microphone at the trailing edge when idle, a red
     stop while recording; with text it gives its place to Send in the
     accent. The empty chat shows the big central mic instead. */
  .composer-mic, .composer-btn { align-items: center; align-self: flex-end; border: 0; border-radius: 50%; display: flex; flex: none; height: 2.1rem; justify-content: center; margin: 0.2rem 0.2rem 0.2rem 0; padding: 0; transition: transform 120ms ease, background-color 120ms ease; width: 2.1rem; }
  .composer-mic { background: transparent; color: var(--c-ink); }
  .chat-unstarted .composer-mic { display: none; }
  .composer-mic:active, .composer-btn:active { transform: scale(0.9); }
  .composer-mic svg { height: 1.35rem; width: 1.35rem; }
  .composer-mic.recording { background: var(--danger); color: white; }
  .composer-mic[disabled], .welcome-mic[disabled] { box-shadow: none; opacity: 0.45; }
  .composer-plus { align-items: center; background: var(--c-field); border: 0; border-radius: 50%; color: var(--c-ink); cursor: pointer; display: flex; height: 2.5rem; justify-content: center; padding: 0; transition: transform 160ms ease, background-color 120ms ease; width: 2.5rem; }
  .composer-plus svg { height: 1.4rem; width: 1.4rem; transition: transform 160ms ease; }
  body.chat-add-open .composer-plus svg { transform: rotate(45deg); }
  .composer-plus:active { transform: scale(0.92); }
  .chat-composer:not(:focus-within) .composer-plus { background: color-mix(in oklch, var(--c-field) 60%, transparent); }
  .composer-wave { color: var(--c-accent); display: none; height: 100%; inset: 0 2.6rem 0 0.85rem; position: absolute; width: calc(100% - 3.45rem); }
  .composer-field.recording .composer-input { opacity: 0; }
  .composer-field.recording .composer-wave { display: block; }
  .composer-btn { background: var(--c-member); color: var(--c-member-ink); }
  .composer-btn[disabled] { opacity: 0.5; }
  .composer-btn svg { height: 1.15rem; width: 1.15rem; }
  .btn-send { display: none; }
  .has-text .btn-send { display: flex; }
  .has-text .btn-mic { display: none; }
  /* Errors only — recording and transcribing speak through the waveform and
     the input's placeholder, so this line stays empty in the normal flow. */
  .voice-status { color: var(--c-muted); display: block; font-size: 0.8rem; padding-bottom: 0.35rem; text-align: center; }
  .voice-status:empty { display: none; }
  .typing[hidden] { display: none; }
  .msg-row[hidden] { display: none; }
  .typing-bubble { display: flex; gap: 0.32rem; padding: 0.78rem 0.9rem; }
  .typing-bubble span { animation: typing-blink 1.2s infinite both; background: var(--c-muted); border-radius: 50%; height: 0.45rem; width: 0.45rem; }
  .typing-bubble span:nth-child(2) { animation-delay: 0.2s; }
  .typing-bubble span:nth-child(3) { animation-delay: 0.4s; }
  @keyframes typing-blink { 0%, 80%, 100% { opacity: 0.25; } 40% { opacity: 1; } }
  .proposal-card { white-space: normal; }
  .proposal-when, .proposal-attendees, .proposal-description { color: var(--c-muted); font-size: 0.92rem; margin: 0.15rem 0 0; }
  .proposal-description { white-space: pre-wrap; }
  .proposal-state { margin: 0.4rem 0 0; }
  .proposal-failed, .proposal-dismissed { color: var(--danger); }
  .proposal-actions { display: flex; gap: 0.5rem; margin-top: 0.55rem; }
  .proposal-actions .button { min-height: 2.2rem; padding: 0.35rem 0.8rem; }
  .chat-markdown p, .chat-markdown ul, .chat-markdown ol, .chat-markdown pre, .chat-markdown blockquote { margin: 0.45em 0; }
  .chat-markdown > :first-child, .assistant-output-text > :first-child { margin-top: 0; }
  .chat-markdown > :last-child, .assistant-output-text > :last-child { margin-bottom: 0; }
  .chat-markdown pre { overflow-x: auto; }
  /* A heading inside one chat entry keeps its meaning but not its size. One
     voice is speaking; a 2rem line halfway through reads as shouting rather
     than structure. The guides share .chat-markdown and keep real headings. */
  :is(.bubble, .assistant-output) h1, :is(.bubble, .assistant-output) h2, :is(.bubble, .assistant-output) h3, :is(.bubble, .assistant-output) h4 { font-size: 1em; font-weight: 650; letter-spacing: normal; margin: 0.45em 0; }

  @media (min-width: 900px) {
    .chat-header { padding-top: 0.45rem; }
    /* Empty-chat presentation changes only the composer. The header,
       room context, and thread keep the standard chat geometry. */
    .chat-shell.chat-unstarted .chat-composer { background: transparent; padding-top: 1.1rem; }
  }
}

@layer utilities {
  .sr-only { clip: rect(0, 0, 0, 0); clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }
}

@layer components {
  @media (max-width: 640px) {
    .page-content { padding-top: 1.25rem; }
  }
}

@layer components {
  .page-content:has(> .conversation-index) { max-width: none; padding: 0; }
  /* The list wears the chat theme too, but only where the theme is the
     subject: the ground it sits on and the unread dot. Rows, names and
     times keep the app's own colours (issue #126). */
  .conversation-index {
    --conversation-accent: oklch(91% 0.2 101);
    background: var(--c-bg, var(--surface));
    color: var(--c-ink, var(--ink));
    margin: 0 auto;
    max-width: 44rem;
    min-height: 100dvh;
    padding: 0 1rem;
    position: relative;
  }
  .conversation-index-header { align-items: center; background: var(--c-bg, var(--surface)); display: flex; height: 3.5rem; margin-bottom: 0.6rem; position: sticky; top: 0; z-index: 10; }
  .conversation-index-family { align-items: center; display: flex; justify-content: space-between; width: 100%; }
  .conversation-index .family-switcher { position: relative; }
  .conversation-index .family-switcher-trigger { background: transparent; border: 0; color: var(--ink); gap: 0.35rem; margin: 0; padding: 0.4rem 0; }
  .conversation-index .family-switcher-trigger > .family-icon { display: none; }
  .family-unread-indicator:not(:has(.family-unread-dot)) { display: none; }
  .conversation-index .family-switcher-current-unread .family-unread-dot { background: var(--c-accent, var(--conversation-accent)); }
  .conversation-index .family-switcher-name { font-size: 1rem; font-weight: 500; }
  .conversation-index-settings { align-items: center; border-radius: 50%; color: var(--ink); display: flex; height: 2.75rem; justify-content: center; width: 2.75rem; }
  @media (hover: hover) and (pointer: fine) { .conversation-index-settings:hover { background: color-mix(in oklch, var(--ink) 6%, transparent); text-decoration: none; } }
  .conversation-index-settings svg { height: 1.55rem; width: 1.55rem; }
  .conversation-list, .recent-conversations { align-content: start; display: grid; }
  .conversation-list { margin-inline: -0.75rem; }
  /* Swipe to delete, the way iMessage does it. The row slides under the
     finger across the plain list, uncovering a red circle that stretches
     with it and keeps the trash in its middle. Past the threshold the pill
     arms: the trash slides to its left end and the phone taps back, so
     letting go there deletes. Letting go before it rests the row on the
     circle, which is a button too. The pill width follows the drag, and one
     custom property drives all of it. */
  .conversation-swipe { --pill: max(2.75rem, calc(var(--swipe, 0px) - 1rem)); overflow: hidden; position: relative; }
  .conversation-swipe.deleting { display: none; }
  .conversation-swipe-sheet { background: var(--c-bg, var(--surface)); position: relative; transform: translateX(calc(var(--swipe, 0px) * -1)); z-index: 1; }
  /* Only while the row settles back or springs open: during the drag it must
     sit exactly under the finger. */
  .conversation-swipe.settling .conversation-swipe-sheet, .conversation-swipe.settling .conversation-delete { transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), width 220ms cubic-bezier(0.2, 0.8, 0.2, 1); }
  .conversation-delete-form { align-items: center; bottom: 0; display: flex; position: absolute; right: 0.5rem; top: 0; }
  .conversation-delete { background: var(--danger); border: 0; border-radius: 999px; color: white; display: block; height: 2.75rem; padding: 0; position: relative; width: var(--pill); }
  /* The trash sits in the middle of the pill, and at its left end once the
     swipe is armed. Both are plain layout positions: the middle follows a
     stretching pill by itself, and the left end does not move at all, so
     nothing animates towards a target that changes every frame. */
  .conversation-delete-icon { display: flex; left: 50%; margin: -0.65rem 0 0 -0.65rem; position: absolute; top: 50%; }
  .conversation-swipe.armed .conversation-delete-icon { left: 1.15rem; margin-left: 0; }
  /* Only the flip itself slides. */
  .conversation-swipe.arming .conversation-delete-icon { transition: left 160ms ease; }
  .conversation-delete svg { height: 1.3rem; width: 1.3rem; }
  .conversation-row { -webkit-tap-highlight-color: transparent; align-items: center; border-radius: 0.75rem; color: var(--ink); display: grid; gap: 0.5rem; grid-template-columns: auto minmax(0, 1fr); min-height: 4.4rem; padding: 0.55rem 0.75rem; transition: background-color 100ms ease; }
  @media (hover: hover) and (pointer: fine) { .conversation-row:hover { background: color-mix(in oklch, var(--ink) 3%, transparent); border-radius: 0.75rem; text-decoration: none; } }
  .conversation-row.pending { background: color-mix(in oklch, var(--ink) 7%, transparent); text-decoration: none; }
  .conversation-row-avatars { border-radius: 50%; display: block; flex: none; height: 2.75rem; width: 2.75rem; }
  .conversation-row-avatars .member-avatar { border: 2px solid var(--paper); border-radius: 50%; font-size: 0.9rem; height: 100%; margin: 0; min-width: 0; width: 100%; }
  .conversation-person-avatar, .conversation-group-avatar { background: color-mix(in oklch, var(--c-accent, var(--conversation-accent)) 14%, var(--surface)); padding: 0.55rem; }
  .chat-photo-settings > .member-avatar { height: 5.5rem; width: 5.5rem; }
  .chat-photo-faces { display: flex; }
  .chat-photo-faces .member-avatar { border: 3px solid var(--paper); font-size: 1.6rem; height: 4.4rem; width: 4.4rem; }
  .chat-photo-faces .member-avatar + .member-avatar { margin-left: -1.2rem; }
  .chat-photo-faces .chat-heading-more { background: color-mix(in oklch, var(--ink) 8%, var(--surface)); color: var(--muted); font-size: 1.1rem; }
  .chat-photo-actions { display: flex; gap: 0.5rem; }
  .chat-photo-actions form { margin: 0; }
  .chat-photo-choose { cursor: pointer; display: inline-block; margin: 0; }
  .chat-details-assistant { background: transparent; object-fit: contain; }
  .chat-person-choice, .chat-choice { cursor: pointer; font-weight: inherit; margin: 0; }
  .chat-person-check, .chat-choice input { accent-color: var(--c-accent, var(--accent)); flex: none; height: 1.3rem; margin-left: auto; width: 1.3rem; }
  .settings-row-text + .chat-person-check, .settings-row-text + input { margin-left: auto; }
  .settings-field select { appearance: auto; }
  .conversation-row-content { display: grid; gap: 0.16rem; min-width: 0; }
  .conversation-row-topline, .conversation-row-bottomline { align-items: center; display: flex; gap: 0.5rem; min-width: 0; }
  .conversation-row-topline strong { flex: 1; font-size: 1rem; font-weight: 560; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .conversation-row-topline time { color: var(--muted); flex: none; font-size: 0.75rem; }
  .conversation-row-preview { color: var(--muted); flex: 1; font-size: 0.86rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .conversation-row-file { color: var(--muted); flex: none; height: 0.95rem; width: 0.95rem; }
  /* Like an unseen story, unread state rings the subject instead of adding a
     separate dot and a second alignment column. The clear inner shadow keeps
     the ring apart from photos and initials. */
  .conversation-row-avatars { isolation: isolate; position: relative; }
  /* The ring is drawn behind the avatar rather than as a box-shadow, because a
     shadow cannot carry a gradient. Its own border paints the sweep and its
     padding paints the clear gap, so the row's geometry does not move. The
     sweep is fixed rather than the chat theme's accent, so unread reads the
     same in every theme. */
  .conversation-row:has(.unread-ring-marker:not([hidden])) .conversation-row-avatars::after {
    background: linear-gradient(var(--c-bg, var(--surface)), var(--c-bg, var(--surface))) padding-box,
                var(--unread-ring) border-box;
    border: 3px solid transparent;
    border-radius: 50%;
    content: "";
    inset: -5px;
    position: absolute;
    z-index: -1;
  }
  .conversation-row.active .conversation-row-avatars::after { content: none; }
  .unread-ring-marker { display: contents; }
  .unread-ring-marker[hidden] { display: none; }
  /* The empty list. Its whole job is to point at the button that ends it, so
     the inline plus is drawn to match the real one, and the invite sits
     apart, below a rule, as the second thing to do rather than the first. */
  /* Underlined, because it is the one tappable word in a sentence and colour
     alone does not say so. */
  .conversation-list-empty a { color: var(--c-accent, var(--accent)); text-decoration: underline; text-underline-offset: 0.2em; }
  /* Nothing to list means these words are the whole screen, so they sit in
     the middle of it — the middle of the room between the navbar and the
     floating button, which is why the padding that clears the button stays. */
  .conversation-list:has(> .conversation-list-empty) { align-content: center; }
  .conversation-list-empty { align-content: start; color: var(--muted); display: grid; gap: 0.35rem; justify-items: center; margin: 0 auto; max-width: 21rem; padding: 1rem; text-align: center; }
  .conversation-list-empty-mark { border-radius: 1rem; height: 3.25rem; opacity: 0.9; width: 3.25rem; }
  .conversation-list-empty strong { color: var(--ink); font-size: 1.05rem; font-weight: 650; margin-top: 0.75rem; }
  .conversation-list-empty p { font-size: 0.9rem; line-height: 1.5; margin: 0; }
  .conversation-list-empty-plus { background: var(--c-own-bubble, var(--conversation-accent)); border-radius: 50%; color: oklch(20% 0.02 101); display: inline-grid; height: 1.15rem; place-items: center; vertical-align: -0.2rem; width: 1.15rem; }
  .conversation-list-empty-plus svg { height: 0.7rem; width: 0.7rem; }
  .conversation-list-empty-invite { border-top: 1px solid var(--line); display: grid; gap: 0.6rem; justify-items: center; margin-top: 2rem; padding-top: 1.75rem; width: 100%; }
  .conversation-list-empty-invite h2 { color: var(--ink); font-size: 0.95rem; margin: 0; }
  .conversation-list-empty-invite .invite-qr { box-shadow: 0 0.4rem 1.4rem rgb(0 0 0 / 0.1); margin: 0.15rem 0; width: min(11rem, 70%); }
  .conversation-list-empty-invite .button { margin-top: 0.2rem; }
  .conversation-new-button { align-items: center; background: var(--c-own-bubble, var(--conversation-accent)); border: 0; border-radius: 50%; bottom: max(1.4rem, env(safe-area-inset-bottom)); box-shadow: 0 0.7rem 1.8rem rgb(0 0 0 / 0.12); color: var(--c-own-bubble-ink, oklch(20% 0.02 101)); cursor: pointer; display: flex; height: 4.35rem; justify-content: center; left: 50%; padding: 0; position: fixed; transform: translateX(-50%); transition: background-color 150ms ease, box-shadow 150ms ease, transform 150ms ease; width: 4.35rem; z-index: 25; }
  .conversation-new-button:active { box-shadow: 0 0.35rem 0.9rem rgb(0 0 0 / 0.12); transform: translateX(-50%) scale(0.94); }
  @media (hover: hover) and (pointer: fine) {
    .conversation-new-button:hover { background: color-mix(in oklch, var(--c-own-bubble, var(--conversation-accent)) 92%, var(--ink)); box-shadow: 0 0.95rem 2.2rem rgb(0 0 0 / 0.18); transform: translateX(-50%) translateY(-0.15rem); }
  }
  .conversation-new-button svg { height: 2rem; width: 2rem; }

  @media (min-width: 641px) {
    .conversation-index { padding: 0 2rem; }
    .conversation-index-header { margin-bottom: 0.9rem; }
  }

  @media (max-width: 899.9px) {
    /* The rail is in the document on every screen and only hidden here, so
       this has to ask whether the list is what you are looking at — otherwise
       a form screen inherits the list's ground and wears a grey panel over
       its own. */
    html:has(.conversation-index):not(:has(.chat-action-page)),
    body:has(.conversation-index):not(:has(.chat-action-page)),
    .page-shell:has(.conversation-index):not(:has(.chat-action-page)) { background: var(--surface); }
    /* Dock the header on mobile web: the index becomes a locked grid and the
       rows scroll inside it, exactly as the rail does on a wide screen and as
       the chat holds its composer. Sticky rides the document scroller, which
       iOS Safari moves under its own toolbars, so the header drifted off with
       the rows. */
    .conversation-index { display: grid; grid-template-rows: 3.5rem minmax(0, 1fr); height: 100dvh; min-height: 0; overflow: hidden; }
    .conversation-index-header { margin-bottom: 0; }
    .conversation-list { min-height: 0; overflow-y: auto; overscroll-behavior-y: contain; padding-bottom: 7rem; padding-top: 0.6rem; }
  }

  @media (min-width: 900px) {
    .rail .conversation-index-header, .conversation-master-layout .conversation-index-header { margin-bottom: 0; }
  }

  .chat-action-page { display: grid; gap: 2rem; margin: 0 auto; max-width: 34rem; }
  /* These screens are a form on a page, not cards on a board, so they sit on
     the chat's own ground rather than the grey the grouped settings need
     (elfmio-pnxz). --surface is white by day but a raised grey by night,
     which left a panel floating behind them; the theme's background is white
     and black, which is what the eye expects either way. */
  body:has(.chat-action-page), body:has(.chat-action-page) .page-shell { background: var(--c-bg, var(--surface)); }
  /* One quiet action, and the invitation itself once there is one. No icon
     badge and no second colour: inviting somebody is not the loudest thing
     on a screen listing the people already here. */
  .chat-invite-card { display: grid; gap: 0.75rem; justify-items: center; padding: 1rem; }
  .chat-invite-qr { background: white; border-radius: 0.6rem; padding: 0.75rem; }
  .chat-invite-qr svg { height: auto; width: 10rem; }
  .chat-invite-link { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.75rem; margin: 0; overflow-wrap: anywhere; text-align: center; }
  .chat-action-page header h1 { margin: 0 0 0.5rem; }
  .chat-action-page header p:last-child { color: var(--muted); }
  .eyebrow { color: var(--muted); font-size: 0.76rem; font-weight: 700; letter-spacing: 0.08em; margin-bottom: 0.45rem; text-transform: uppercase; }
  .chat-action-list { display: grid; }
  .chat-action-row { align-items: center; color: var(--ink); display: grid; gap: 0.9rem; grid-template-columns: auto 1fr; min-height: 4.6rem; padding: 0.65rem 0; }
  .chat-action-row + .chat-action-row { border-top: 1px solid var(--line); }
  .chat-action-row:hover { text-decoration: none; }
  .chat-action-row > span:last-child { display: grid; }
  .chat-action-row strong { font-weight: 600; }
  .chat-action-row small { color: var(--muted); }
  .chat-action-icon { align-items: center; background: var(--accent-soft); border-radius: 50%; color: var(--accent); display: flex; height: 2.8rem; justify-content: center; width: 2.8rem; }
  .chat-action-icon svg { height: 1.4rem; width: 1.4rem; }
  .conversation-file-form { display: grid; gap: 1rem; }
  .conversation-file-picker { align-items: center; border: 1.5px dashed var(--line); border-radius: 1rem; cursor: pointer; display: flex; flex-direction: column; gap: 0.3rem; justify-content: center; min-height: 13rem; padding: 2rem; text-align: center; }
  .conversation-file-picker:hover { background: var(--accent-soft); border-color: var(--accent); }
  .conversation-file-picker svg { color: var(--accent); height: 2.3rem; margin-bottom: 0.4rem; width: 2.3rem; }
  .conversation-file-picker small { color: var(--muted); }
  .conversation-file-picker input { margin-top: 1rem; }
}

@layer chat {
  .chat-header { grid-template-columns: 2.5rem minmax(0, 1fr) 2.5rem; }
  .chat-header .conversation-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* A file sits in the thread where it was added. A picture fills its own
     bubble, edge to edge, because a photo cropped by padding reads as a
     mistake; everything else keeps the filename and the reading state. */
  .bubble-file { background: var(--c-family-bubble); border-bottom-left-radius: 0.35rem; color: var(--c-family-bubble-ink); overflow: hidden; padding: 0.4rem 0.55rem; }
  /* A picture bubble owns its full width from the first paint. Left to
     shrink-to-fit, it hugs the filename until the image bytes arrive and
     then widens — growing the whole row after render, which shifts the
     thread under the reader however well the image's own box is reserved. */
  .bubble-file:has(.file-preview) { width: min(76%, 22rem); }
  .msg-row-own .bubble-file { background: var(--c-own-bubble); border-bottom-left-radius: 1.25rem; border-bottom-right-radius: 0.35rem; color: var(--c-own-bubble-ink); }
  .file-preview { display: block; margin: -0.4rem -0.55rem 0.35rem; }
  .file-preview:hover { text-decoration: none; }
  .file-image { border-radius: 0.9rem; display: block; height: auto; max-height: 22rem; object-fit: cover; width: 100%; }
  /* A file whose analysis hasn't produced dimensions renders without the
     width/height attributes that reserve its box — pin those to a fixed
     ratio (cover crops the excess) so their late load can never shift the
     thread. */
  .file-image:not([width]) { aspect-ratio: 4 / 3; }
  /* Outranks .chat-shell a, so the filename takes the bubble's ink rather
     than the link accent — which on an own-bubble is the background. */
  .bubble-file .file-line { align-items: center; color: inherit; display: flex; gap: 0.4rem; min-width: 0; }
  .file-line:hover { text-decoration: none; }
  .file-icon { flex: none; height: 1.15rem; width: 1.15rem; }
  .file-name { font-size: 0.86rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .file-state { display: block; font-size: 0.72rem; opacity: 0.75; }
  .file-state-failed { color: var(--danger); opacity: 1; }
  .msg-row[data-role="system"] { justify-content: center; margin-top: 0.8rem; }
  .system-message { color: var(--c-muted); font-size: 0.74rem; max-width: 80%; text-align: center; }
}
