* { box-sizing: border-box; }

/* ============================================================================
   Fonts

   Self-hosted rather than fetched from Google. A webfont request carries the
   reader's IP and the page they are on to a third party on every load, which
   is a subprocessor to name in the privacy policy and a consent argument to
   have, in exchange for two files we can serve ourselves in 132K.

   Both families are variable, so one file spans the whole weight range and
   the 550/650 weights the design uses are real instances rather than
   synthesised ones. Latin and Latin-Extended only; the browser fetches the
   -ext file solely if the page actually uses those codepoints.
   ========================================================================= */

@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/instrument-sans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/instrument-sans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/instrument-sans-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Sans';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/instrument-sans-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url(fonts/jetbrains-mono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url(fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ============================================================================
   Design tokens
   ========================================================================= */

:root {
  --font-ui: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  /* The document's own voice, and the thing that makes a page read as a
     document rather than as the tool it is being read in. Six rules already
     asked for --font-doc — the article, the quote on every thread card, the
     rich editor — and the token was never defined, so all six quietly resolved
     to the UI sans and the distinction the rules were written to draw was not
     on screen anywhere. These are text faces with true italics and a large
     x-height, none of them fetched: Source Serif where the reader has it, then
     whatever their system ships — New York or Iowan Old Style or Charter on a
     Mac, Sitka or Cambria on Windows — and Georgia last, which is everywhere.
     Nothing here downloads, so a portal that worked offline still does. */
  --font-doc: 'Source Serif 4', 'Source Serif Pro', ui-serif, 'Iowan Old Style',
              Charter, 'Bitstream Charter', 'Sitka Text', Cambria, Georgia, serif;

  --bg: #FAF9F6;
  --bg-soft: #F4F2ED;
  --card: #FFFFFF;
  --border: #E6E2DA;
  --border-soft: #F0EDE6;
  --text: #191817;
  --text-soft: #736E66;
  --accent: #4F46D6;
  --on-accent: #FFFFFF;
  --accent-tint: rgba(79, 70, 214, 0.08);
  --accent-wash: #F5F4FE;
  --hl: #FCEDBE;
  --hl-strong: #E8B93A;
  --hl-resolved: rgba(62, 142, 90, 0.14);
  --veil: rgba(250, 249, 246, 0.92);
  --shadow-1: var(--e1);
  --shadow-2: var(--e4);
  --success-bg: #F0F5F1;
  --success-border: #DBE8DE;

  --surface-sunken: #F2F0EA;
  --surface-footer: #F0EDE6;
  --surface-hover: #E9E5DC;
  --surface-hover-card: #F7F5F0;
  --surface-card-foot: #FCFBF9;
  --border-strong: #E0DBD1;
  --border-hover: #C9C2B5;
  --border-dashed: #D5CFC3;

  --text-body: #33302B;
  --text-mid: #4A453D;
  --text-quiet: #5E5A52;
  --text-faint: #716D64;

  --ink: #191817;
  --ink-raised: #33302B;
  --ink-line: #46423B;
  --ink-text: #FAF9F6;
  --ink-text-soft: #CFC9BE;
  --ink-accent: #FBE59B;

  --attn-bg: #FFF8E3;
  --attn-border: #F7E9C4;
  --attn-text: #7A5D0E;
  --attn-text-quiet: #82703C;
  --attn-chip: #F2C64B;
  --attn-chip-text: #3D2F06;

  /* The chip that says a document is still drawing itself. Its own three
     tokens rather than the attention amber: nothing is wrong, something is
     simply happening, and amber would read as a warning about the document. */
  --snap-bg: #EFEEFC;
  --snap-border: #DDDAF7;
  --snap-text: #4A43A8;
  --snap-dot: #6C63E8;

  --ok: #377D4F;
  --ok-bg: #F0F5F1;
  --ok-border: #DBE8DE;
  --warn-dot: #E8B93A;
  --danger: #A34A45;
  --danger-bg: #FDF0F0;
  --idle-dot: #C9C2B5;
  --sync-dot: #3E8E5A;

  --resolved-summary: #EFEDE6;

  /* ---- diff (the editor's Changes view) ---- */
  --diff-add-bg: #F0F8F2;
  --diff-add-text: #2F6B45;
  --diff-add-gutter: #5D8F72;
  --diff-del-bg: #FDF1F1;
  --diff-del-text: #8C3F3A;
  --diff-del-gutter: #A56B67;

  --av-1: #4F46D6;
  --av-2: #3E7A9E;
  --av-3: #8B5A9E;
  --av-4: #996B37;
  --av-5: #398353;
  --av-6: #A34A45;
  /* Initials sit on whichever --av-* the person hashed to, so the ink has to
     work on all six. White does in this theme; in dark the palette lifts to
     pastels and white fell to 2.6:1 on every one of them. */
  --on-av: #FFFFFF;

  /* Elevation, four steps. There were a dozen one-off shadows before this —
     0 1px 2px at .05, .06 and .07 for the same job — and every one of them was
     a near-black at low alpha, which is a shadow only on a pale surface. In
     dark mode they landed on #1A1917 and vanished, so cards, popovers and
     dialogs all sat flat against the page with nothing but a border to say
     which was on top. The dark set below is opaque black at high alpha, the
     only thing that reads as depth on a dark ground. */
  --e1: var(--e1);
  --e2: 0 4px 12px rgba(25, 24, 23, 0.10);
  --e3: 0 12px 30px rgba(25, 24, 23, 0.16);
  --e4: 0 28px 70px rgba(25, 24, 23, 0.28);

  /* Status and data hues. Analytics and the tracker were carrying these as
     about sixty raw hex literals spread across their rules, plus a hand-kept
     set of [data-theme="dark"] overrides to correct the ones used as text —
     which is why those two screens drifted out of the system every time it
     moved. Four hues, each in three parts: the fill for bars, pills and
     borders; the ink that sits legibly *on* that fill; and a separate darker
     ink for the same hue used as text on the page, because a colour bright
     enough to read as a bar is not dark enough to read as a word. Only the
     text inks change between themes; the fills carry their own ink with them.
     Every pair here clears 4.5:1 — several did not before, including white on
     the red fill at 3.25, which is why red takes a dark ink like the other
     three rather than the white it used to wear. */
  --data-red: #E06C5A;    --on-data-red: #3C1D18;   --data-red-ink: #C54D3C;
  --data-amber: #E2B93B;  --on-data-amber: #3B2F05; --data-amber-ink: #8F7017;
  --data-blue: #6BA3E8;   --on-data-blue: #0D2340;  --data-blue-ink: #3A76BF;
  --data-green: #5EBD8A;  --on-data-green: #0E3322; --data-green-ink: #36825D;
  --data-grey: #98A1AC;   --on-data-grey: #1D232A;

  --r-xs: 5px;
  --r-sm: 7px;
  --r-md: 9px;
  --r-lg: 11px;
  --r-xl: 13px;
  --r-2xl: 16px;
  --r-pill: 20px;

  --rail-w: 272px;
  --pane-w: 380px;
  --measure: 640px;
  /* How far a code block or a table may reach past the prose measure. Prose is
     read a line at a time and wants the narrow column; a terminal transcript
     or a table is scanned across, and the same column only clips it — the
     README's own quick-start block was 642px of content inside 590px of box —
     while the gutter beside it sat empty. The gutter is whatever the three
     panes leave over, so it is measured from their live widths: both are
     rewritten on :root as the reader drags either divider, and the clamp
     collapses the whole thing to zero before it could ever push the column
     off-centre or overrun the pane. */

  /* --measure bounds prose; this bounds the screens that are not prose. The
     For-you queue, Tasks and Analytics each declared max-width:none and grew
     to whatever the monitor was, so on a wide display a task row ran a metre
     of empty space between its title and its due date. Wider than --measure
     because these are scanned in columns rather than read in lines, but
     bounded, so every screen in the product now sits in a column of some
     kind. Applied as padding rather than max-width: it centres a pane without
     touching how any child inside it lays itself out. */
  --page-max: 1120px;
  --tap: 44px;
}

:root[data-theme="dark"] {
  --bg: #1A1917;
  --bg-soft: #201E1B;
  --card: #2A2723;
  --border: #322E29;
  --border-soft: #2B2823;
  --text: #EFEBE3;
  --text-soft: #948D81;
  --accent: #9A93F5;
  --on-accent: #262626;
  --accent-tint: rgba(154, 147, 245, 0.14);
  --accent-wash: rgba(154, 147, 245, 0.12);
  --hl: rgba(242, 198, 75, 0.20);
  --hl-strong: #C79A2E;
  --hl-resolved: rgba(91, 169, 122, 0.18);
  --veil: rgba(26, 25, 23, 0.92);
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-2: 0 18px 44px rgba(0, 0, 0, 0.55);
  --success-bg: rgba(91, 169, 122, 0.14);
  --success-border: rgba(91, 169, 122, 0.32);

  --surface-sunken: #221F1D;
  --surface-footer: #262320;
  --surface-hover: #2A2723;
  --surface-hover-card: #322E29;
  --surface-card-foot: #262320;
  --border-strong: #3B362F;
  --border-hover: #4A453D;
  --border-dashed: #3B362F;

  --text-body: #CFC9BE;
  --text-mid: #D8D2C6;
  --text-quiet: #B3ABA0;
  --text-faint: #938D82;

  --ink: #FBE59B;
  --ink-raised: #322E29;
  --ink-line: #46423B;
  --ink-text: #3D2F06;
  --ink-text-soft: #6A665F;
  --ink-accent: #3D2F06;

  --attn-bg: rgba(242, 198, 75, 0.12);
  --attn-border: rgba(242, 198, 75, 0.22);
  --attn-text: #E7C765;
  --attn-text-quiet: #9B958A;
  --attn-chip: #F2C64B;
  --attn-chip-text: #3D2F06;

  --snap-bg: rgba(154, 147, 245, 0.14);
  --snap-border: rgba(154, 147, 245, 0.30);
  --snap-text: #B9B3F8;
  --snap-dot: #9A93F5;

  --ok: #5BA97A;
  --ok-bg: #262320;
  --ok-border: #3B362F;
  --warn-dot: #C79A2E;
  --danger: #D98C86;
  --danger-bg: rgba(163, 74, 69, 0.16);
  --idle-dot: #4A453D;
  --sync-dot: #5BA97A;

  --resolved-summary: #262320;

  --diff-add-bg: rgba(91, 169, 122, 0.13);
  --diff-add-text: #8FCBA5;
  --diff-add-gutter: #6E9C80;
  --diff-del-bg: rgba(163, 74, 69, 0.15);
  --diff-del-text: #E09B95;
  --diff-del-gutter: #B08480;

  --av-1: #9A93F5;
  --av-2: #4E93BC;
  --av-3: #B078C6;
  --av-4: #C29455;
  --av-5: #5BA97A;
  --av-6: #D98C86;
  --on-av: #1A1917;

  --e1: 0 1px 2px rgba(0, 0, 0, 0.40);
  --e2: 0 4px 12px rgba(0, 0, 0, 0.50);
  --e3: 0 12px 30px rgba(0, 0, 0, 0.60);
  --e4: 0 28px 70px rgba(0, 0, 0, 0.70);

  /* Same four hues; only the text inks lift for a dark surface. */
  --data-red-ink: #E88070;
  --data-amber-ink: #E2B93B;
  --data-blue-ink: #8FBAEE;
  --data-green-ink: #7BCCA0;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #1A1917;
    --bg-soft: #201E1B;
    --card: #2A2723;
    --border: #322E29;
    --border-soft: #2B2823;
    --text: #EFEBE3;
    --text-soft: #948D81;
    --accent: #9A93F5;
    --on-accent: #262626;
    --accent-tint: rgba(154, 147, 245, 0.14);
    --accent-wash: rgba(154, 147, 245, 0.12);
    --hl: rgba(242, 198, 75, 0.20);
    --hl-strong: #C79A2E;
    --hl-resolved: rgba(91, 169, 122, 0.18);
    --veil: rgba(26, 25, 23, 0.92);
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-2: 0 18px 44px rgba(0, 0, 0, 0.55);
    --success-bg: rgba(91, 169, 122, 0.14);
    --success-border: rgba(91, 169, 122, 0.32);

    --surface-sunken: #221F1D;
    --surface-footer: #262320;
    --surface-hover: #2A2723;
    --surface-hover-card: #322E29;
    --surface-card-foot: #262320;
    --border-strong: #3B362F;
    --border-hover: #4A453D;
    --border-dashed: #3B362F;

    --text-body: #CFC9BE;
    --text-mid: #D8D2C6;
    --text-quiet: #B3ABA0;
    --text-faint: #938D82;

    --ink: #FBE59B;
    --ink-raised: #322E29;
    --ink-line: #46423B;
    --ink-text: #3D2F06;
    --ink-text-soft: #6A665F;
    --ink-accent: #3D2F06;

    --attn-bg: rgba(242, 198, 75, 0.12);
    --attn-border: rgba(242, 198, 75, 0.22);
    --attn-text: #E7C765;
    --attn-text-quiet: #9B958A;
    --attn-chip: #F2C64B;
    --attn-chip-text: #3D2F06;

    --snap-bg: rgba(154, 147, 245, 0.14);
    --snap-border: rgba(154, 147, 245, 0.30);
    --snap-text: #B9B3F8;
    --snap-dot: #9A93F5;

    --ok: #5BA97A;
    --ok-bg: #262320;
    --ok-border: #3B362F;
    --warn-dot: #C79A2E;
    --danger: #D98C86;
    --danger-bg: rgba(163, 74, 69, 0.16);
    --idle-dot: #4A453D;
    --sync-dot: #5BA97A;

    --resolved-summary: #262320;

    --diff-add-bg: rgba(91, 169, 122, 0.13);
    --diff-add-text: #8FCBA5;
    --diff-add-gutter: #6E9C80;
    --diff-del-bg: rgba(163, 74, 69, 0.15);
    --diff-del-text: #E09B95;
    --diff-del-gutter: #B08480;

    --av-1: #9A93F5;
    --av-2: #4E93BC;
    --av-3: #B078C6;
    --av-4: #C29455;
    --av-5: #5BA97A;
    --av-6: #D98C86;
  --on-av: #1A1917;

  --e1: 0 1px 2px rgba(0, 0, 0, 0.40);
  --e2: 0 4px 12px rgba(0, 0, 0, 0.50);
  --e3: 0 12px 30px rgba(0, 0, 0, 0.60);
  --e4: 0 28px 70px rgba(0, 0, 0, 0.70);

    /* Same four hues; only the text inks lift for a dark surface. */
    --data-red-ink: #E88070;
    --data-amber-ink: #E2B93B;
    --data-blue-ink: #8FBAEE;
    --data-green-ink: #7BCCA0;
  }
}

/* ============================================================================
   Base
   ========================================================================= */

html, body {
  margin: 0;
  height: 100%;
  font-family: var(--font-ui);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  /* The app is exactly one viewport tall and every pane scrolls inside itself,
     so the document has nowhere to go. Without this, carrying on scrolling at
     the end of a pane chains out to the page: macOS and iOS rubber-band the
     whole layout up and show a band of empty background beneath it, and Android
     reads the same gesture as pull-to-refresh. Nothing is broken when that
     happens, but one looks like the app has run out and the other reloads it.
     Vertical only — the shorthand would also disable the horizontal swipe the
     browser uses to go back. */
  overscroll-behavior-y: none;
}

button { color: inherit; font-family: inherit; }

.ic {
  width: 15px;
  height: 15px;
  flex: none;
}

.hidden { display: none !important; }

/* Available to screen readers, invisible on screen. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 8px;
  top: -60px;
  z-index: 200;
  padding: 10px 16px;
  background: var(--card);
  color: var(--text);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2), 0 0 0 2px var(--accent);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus { top: 8px; }

/* One visible focus treatment for everything, keyboard-only so pointer users
   never see a ring they did not ask for. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}
.chip:focus-visible,
.fy-row:focus-visible,
.file:focus-visible,
.sr-row:focus-visible,
.outline-row:focus-visible { outline-offset: -2px; }
/* On the ink toolbar the accent ring disappears — use the amber instead. */
#sel-bar :focus-visible { outline-color: var(--ink-accent); }
mark.mdc-hl:focus-visible { outline-offset: 1px; }

kbd {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-faint);
}

/* Shared quiet button used across the chrome */
.ghost-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border-strong);
  background: var(--card);
  border-radius: var(--r-sm);
  padding: 6px 11px;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--text-mid);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease;
}
.ghost-btn:hover { border-color: var(--accent); color: var(--accent); }

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: none;
  border: 0;
  background: transparent;
  border-radius: var(--r-md);
  color: var(--text-soft);
  cursor: pointer;
  position: relative;
  transition: background 0.12s ease, color 0.12s ease;
}
.icon-btn:hover { background: var(--surface-hover); color: var(--text); }

button.primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--r-md);
  padding: 7px 14px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: filter 0.12s ease;
}
button.primary:hover { filter: brightness(1.08); }
button.primary:disabled { opacity: 0.6; cursor: default; }
button.primary kbd { color: var(--on-accent); opacity: 0.75; font-size: 10px; }

/* ============================================================================
   Pressed
   ========================================================================= */

/*
 * There were 113 :hover rules in this stylesheet and not one :active. Nothing
 * in the product acknowledged being pressed — and hover is a pointer's luxury,
 * so on a phone every tap was silent until whatever it triggered came back.
 * The gap between finger and result is where an interface feels cheap.
 *
 * Two treatments, because one does not fit both shapes. A button is an object
 * you push, so it takes a small scale — enough to read as give, not enough to
 * disturb what is beside it. A full-width row is not an object; scaling it
 * looks like a glitch, so it deepens its surface instead. Both land inside
 * 100ms, which is roughly the window in which a response still feels like part
 * of the same gesture rather than an answer to it.
 */
button.primary:active,
.ghost-btn:active,
.icon-btn:active,
.text-btn:active,
.chip:active,
.rt-btn:active,
.tk-ai-btn:active,
.needs-approval:active {
  transform: scale(0.97);
  transition-duration: 60ms;
}
/* Rows deepen rather than shrink. */
.fy-row:active,
.file:active,
.sr-row:active,
.outline-row:active,
.dir-toggle:active,
.tk-group-head:active,
.repo-head:active {
  background: var(--surface-hover);
  transition-duration: 60ms;
}
/* The dark toolbar has its own ground, so borrow its raised tone rather than
   the page's hover colour, which would flash light against it. */
#sel-bar .sel-item:active { background: var(--ink-raised); }

/* A press is a transform, and a transform is exactly what somebody asking for
   less motion is asking to be spared. They keep the surface change, which
   carries the same information without moving anything. */
@media (prefers-reduced-motion: reduce) {
  button.primary:active,
  .ghost-btn:active,
  .icon-btn:active,
  .text-btn:active,
  .chip:active,
  .rt-btn:active,
  .tk-ai-btn:active,
  .needs-approval:active {
    transform: none;
    filter: brightness(0.94);
  }
}

/* --breakout lives here rather than on :root, and that placement is the whole
   point. A custom property is substituted where it is *declared*, not where it
   is used, so a --breakout computed on :root would bake in whatever --pane-now
   was there and inherit that number down unchanged — #app.pane-closed setting
   --pane-now afterwards would arrive far too late to matter. Declared on the
   same element that carries the state class, both resolve together: with the
   discussion pane shut there is no pane to leave room for, and at 1280 that is
   the difference between a code block at the prose measure with 218px of empty
   gutter either side of it, and one that uses the space. */
#app {
  --pane-now: var(--pane-w);
  --breakout: clamp(0px, (100vw - var(--rail-w) - var(--pane-now) - var(--measure)) / 2 - 10px, 96px);
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr) var(--pane-w);
  grid-template-rows: minmax(0, 100%);
  height: 100vh;
}

/* ============================================================================
   Left rail
   ========================================================================= */

#files-pane {
  position: relative;
  background: var(--bg-soft);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Drag the rail wider when a nested folder runs deep, or narrower to give the
   document its room back — the same handle as the discussion pane, mirrored to
   the far edge. Below 1280px the rail is a fixed width or a drawer, so it goes
   away with the layout it belongs to. */
#rail-resize {
  position: absolute;
  top: 0;
  bottom: 0;
  right: -3px;
  width: 7px;
  z-index: 15;
  cursor: col-resize;
  background: transparent;
}
#rail-resize::after {
  content: '';
  position: absolute;
  inset: 0 3px;
  background: var(--accent);
  opacity: 0;
  transition: opacity 0.12s ease;
}
#rail-resize:hover::after,
#rail-resize.dragging::after,
#rail-resize:focus-visible::after { opacity: 0.55; }
#rail-resize:focus-visible { outline: none; }
@media (max-width: 1279px) {
  #rail-resize { display: none; }
}

.rail-head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 16px 14px 12px;
  flex: none;
}
.mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 8px;
  background: var(--ink);
  color: var(--ink-accent);
}
.mark .ic { width: 15px; height: 15px; }
.ws-id { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ws-name {
  font-size: 13.5px;
  font-weight: 650;
  color: var(--text);
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-sub {
  font-size: 11px;
  color: var(--text-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rail-head .icon-btn { margin-left: auto; width: 26px; height: 26px; }

#search-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  width: calc(100% - 28px);
  margin: 0 14px;
  padding: 8px 10px;
  flex: none;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  transition: border-color 0.12s ease;
}
#search-trigger:hover { border-color: var(--border-hover); }
#search-trigger .st-icon { display: inline-flex; color: var(--text-soft); }
#search-trigger .st-icon .ic { width: 14px; height: 14px; }
.st-label { font-size: 13px; color: var(--text-soft); }
.st-kbd { margin-left: auto; }

#for-you { padding: 14px 6px 0; flex: none; }
.rail-label {
  padding: 4px 8px 6px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.fy-row {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 7px 9px;
  border: 0;
  background: transparent;
  border-radius: var(--r-md);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  color: var(--text-mid);
  transition: background 0.12s ease;
}
.fy-row:hover { background: var(--surface-hover); }
.fy-row.attn { background: var(--attn-bg); }
.fy-row.attn:hover { filter: brightness(0.98); }
.fy-row.active { box-shadow: inset 0 0 0 1px var(--border-hover); }
/* Fixed width, whether or not anything is in it. The Analytics row gets its
   icon from an async scope fetch rather than from the render pass that sets the
   others, so when that had not landed the span collapsed to nothing and the
   label slid left into the icon column — one row out of five out of alignment,
   which is the kind of thing you see before you can say what it is. Reserving
   the column makes the rail hold its shape regardless. */
.fy-row .fy-icon { display: inline-flex; justify-content: center; width: 18px; flex: none; color: var(--text-soft); }
.fy-row .fy-icon .ic { width: 14px; height: 14px; }
.fy-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex: none;
  border-radius: var(--r-xs);
  background: var(--attn-chip);
  color: var(--attn-chip-text);
  font-size: 10.5px;
  font-weight: 800;
}
.fy-label { flex: 1; min-width: 0; font-weight: 550; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fy-row.attn .fy-label { color: var(--attn-text); }
.fy-count { font-size: 11.5px; color: var(--text-soft); }
.fy-row.attn .fy-count { font-size: 11.5px; font-weight: 650; color: var(--attn-text); }
.fy-row:not(.attn) .fy-count:empty { display: none; }

.rail-div { height: 1px; background: var(--border); margin: 12px 14px; flex: none; }

/* ---------- file tree ---------- */

#file-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 6px 12px;
}

/* Three stacked lines — name, origin, actions — so a long repository or branch
   name gets the sidebar's full width instead of competing with the controls
   for what is left of one row. */
.repo-head {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  width: 100%;
  margin-top: 6px;
  padding: 7px 8px 5px;
  border-radius: var(--r-sm);
  color: var(--text);
  transition: background 0.12s ease;
}
.repo-head:first-child { margin-top: 0; }
.repo-head:hover { background: var(--surface-hover-card); }

/* The repository whose document is open — a quiet "you are here" marker: an
   accent rail and accent-tinted name, matching the active file card below. */
.repo-head.current {
  background: var(--accent-tint);
}
.repo-head.current::before {
  content: '';
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--accent);
}
.repo-head.current .repo-name { color: var(--accent); }
.repo-head.current:hover { background: var(--accent-tint); }

.repo-toggle {
  width: 100%;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  border: 0;
  background: transparent;
  padding: 0;
  font-family: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
/* Nudged down so it reads against the name rather than the gap below it. */
.repo-head .chev {
  flex: none;
  margin-top: 2px;
  display: inline-flex;
  color: var(--text-faint);
  transition: transform 0.15s ease;
}
.repo-head .chev .ic { width: 13px; height: 13px; }
.repo-head.open .chev { transform: rotate(90deg); }

/* Name and meta stack vertically so the repository name owns the full width
   and only truncates when it genuinely runs out of room. */
.repo-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* The count rides with the name: it is a fact about this repository, and on
   its own line it would read as a fact about the branch. */
.repo-title {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.repo-name {
  flex: 0 1 auto;
  font-size: 12.5px;
  font-weight: 650;
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.repo-sub {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
}
.repo-status {
  flex: none;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--text-faint);
  white-space: nowrap;
}
.repo-branch {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  padding: 0 5px;
  border-radius: var(--r-xs, 5px);
  background: var(--surface-hover-card, rgba(25, 24, 23, 0.05));
  color: var(--text-soft);
  font-size: 10px;
  font-weight: 600;
  line-height: 1.6;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.repo-branch .ic { width: 10px; height: 10px; flex: none; color: var(--text-faint); }
.repo-branch-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sync-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--sync-dot);
  flex: none;
}
.sync-dot.idle { background: var(--idle-dot); }

/* Line three: state on the left, controls on the right. The controls hold their
   place at all times — a row that reflows when the pointer arrives is a row you
   misclick — and brighten on hover, so a sidebar at rest stays quiet. Indented
   past the chevron to sit under the name. */
.repo-tools {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: 21px;
  min-height: 26px;
}
.repo-tools .spacer { flex: 1 1 auto; }
/* Sync state and writability read as one phrase, so they travel together. */
.repo-state {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
}
.repo-remove, .repo-people, .repo-refresh {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 0;
  background: transparent;
  color: var(--text-soft);
  border-radius: var(--r-xs);
  cursor: pointer;
  opacity: 0.55;
  transition: color 0.12s ease, background 0.12s ease, opacity 0.12s ease;
}
.repo-remove .ic, .repo-people .ic { width: 13px; height: 13px; }
.repo-refresh .ic { width: 14px; height: 14px; }
.repo-head:hover .repo-remove,
.repo-head:hover .repo-people,
.repo-head:hover .repo-refresh,
.repo-tools:focus-within .repo-remove,
.repo-tools:focus-within .repo-people,
.repo-tools:focus-within .repo-refresh,
.repo-refresh.spinning { opacity: 1; }
/* Managing people is not a destructive act, so it does not turn red. */
.repo-people:hover { color: var(--text); background: var(--bg-soft); }
.repo-remove:hover { color: var(--danger); background: var(--danger-bg); }
.repo-refresh:hover { color: var(--accent); background: var(--accent-tint); }
.repo-refresh.spinning { color: var(--accent); cursor: default; }
.repo-refresh.spinning .ic { animation: mdc-spin 0.7s linear infinite; }
@keyframes mdc-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .repo-refresh.spinning .ic { animation-duration: 1.6s; }
}
.repo-head .repo-pill {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  font-size: 10.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--attn-chip-text);
  background: var(--attn-chip);
  border-radius: 9px;
}

/* Nesting is an indent, not a wrapper: `--depth` comes in on the row and both
   folders and documents step in by the same amount. Capped, so a deeply buried
   path narrows the rail rather than walking the names off the edge of it. */

/* The documents belong to the repository named above them, so the whole tree
   starts one step in. Without it the first folder's chevron lands exactly where
   the repository's own chevron is and the two read as siblings. Single mode has
   no repository row to sit under, so the tree there stays where it is. */
.repo-files { --tree-base: 12px; }

.dir {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 9px 3px calc(8px + var(--tree-base, 0px) + min(var(--depth, 0) * 12px, 84px));
  font-size: 10.5px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}
.dir-toggle {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  border: 0;
  background: transparent;
  padding: 2px;
  margin: -2px;
  border-radius: var(--r-xs);
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.dir-toggle:hover { color: var(--text-soft); }
.dir .chev { display: inline-flex; color: var(--text-faint); transition: transform 0.15s ease; }
.dir .chev .ic { width: 11px; height: 11px; }
.dir.open .chev { transform: rotate(90deg); }
.dir-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.file {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 9px 6px calc(22px + var(--tree-base, 0px) + min(var(--depth, 0) * 12px, 84px));
  border: 0;
  border-radius: var(--r-md);
  background: transparent;
  font-family: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  color: var(--text-mid);
  transition: background 0.12s ease;
}
.file:hover { background: var(--surface-hover); }

.fname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The card is the open document, so it stands where that document stands —
   indented with it rather than pinned to the edge of the rail. Its own row then
   keeps only its base padding, or the indent would be counted twice, and the
   outline inside starts under the document's name instead of out to its left. */
.file-card {
  /* The open document reads as the selected row of a tree, not as a card
     floating above one. An opaque fill with a shadow and a full ring cut the
     tree it sits in, and the heavier it was the more it looked dropped in from
     somewhere else. Tinted instead, with the 3px accent rail
     .repo-head.current already uses for this exact meaning. */
  background: var(--accent-wash);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  margin: 2px 0 2px calc(var(--tree-base, 0px) + min(var(--depth, 0) * 12px, 84px));
  overflow: hidden;
  /* Not sticky. It was, back when the outline inside it was capped and the card
     was guaranteed to fit; uncapped it can be taller than the list, and a
     sticky box taller than its scroller pins its top and hides its own bottom.
     It scrolls with the tree it belongs to. Relative because sticky used to be
     what gave the accent rail below something to be absolute against, and
     dropping one without the other left the rail measuring itself from
     whichever ancestor happened to be positioned. */
  position: relative;
}
.file-card::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--accent);
}
.file-card .file { padding-left: 22px; color: var(--accent); font-weight: 650; }
.file-card .file:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }


.draft-pill {
  flex: none;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--r-xs);
  padding: 0 4px;
  opacity: 0.85;
}
.draft-pill.review {
  color: var(--text-quiet);
  border-color: var(--border-hover);
}

.count-pill {
  font-size: 10.5px;
  font-weight: 650;
  color: var(--text-quiet);
  background: var(--surface-hover);
  border-radius: var(--r-xs);
  padding: 1px 5px;
  flex: none;
}
.count-pill.attn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 17px;
  height: 17px;
  font-weight: 700;
  color: var(--attn-chip-text);
  background: var(--attn-chip);
}

/* document outline, nested under the active file */
.outline {
  border-top: 1px dashed var(--border-soft);
  padding: 5px 9px 7px 22px;
  /* Deliberately not its own scroll region. This was capped and scrollable for
     a while, which did keep the card inside the list — at the cost of a second
     scroller nested in the first, so a drag over the headings moved them and
     the same drag two pixels lower moved the tree. One rail, one scroll: the
     outline flows at its natural height and the whole thing moves together. */
}
.outline-sec { display: flex; align-items: center; }
.outline-sec .outline-row { flex: 1; min-width: 0; }
.outline-fold {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex: none;
  border: 0;
  background: none;
  color: var(--text-faint);
  border-radius: var(--r-xs);
  cursor: pointer;
}
.outline-fold .ic { width: 11px; height: 11px; transition: transform 0.15s ease; }
.outline-fold[aria-expanded='true'] .ic { transform: rotate(90deg); }
.outline-fold:hover { color: var(--text); background: var(--surface-hover); }
.outline-row {
  display: block;
  width: 100%;
  border: 0;
  background: none;
  text-align: left;
  font-family: inherit;
  font-size: 12px;
  color: var(--text-soft);
  padding: 3px 0 3px 9px;
  border-left: 2px solid transparent;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: color 0.12s ease, border-color 0.12s ease;
}
.outline-row.sub { padding-left: 18px; }
.outline-row:hover { color: var(--text); }
.outline-row.active { color: var(--accent); font-weight: 600; border-left-color: var(--accent); }

/* The rail on a phone.
 *
 * It is a drawer you pull out to get somewhere else, so everything in it is a
 * thing to hit with a thumb — and desktop had sized them all for a cursor.
 * Folder rows were 17px tall, the theme and new-document buttons 26px, the
 * search field 34px. The target to clear is 24×24 CSS px, not the 44pt figure
 * that belongs to native app UI: sizing a file list to 44 gave 48px folder rows
 * and a tree you scrolled twice as far to read.
 *
 * The open document's block was the other half of it. With no collapse on the
 * outline any more, the card was taking 95% of the list it shares with the file
 * tree — 340px of headings sitting on top of 19px of files, in a drawer whose
 * entire job is moving between files. Capping the outline would only trade that
 * for a small scroller nested inside the list's own scroller, which is worse on
 * touch than either. So on a phone the card stops sticking and the outline
 * stops scrolling itself: the whole rail becomes one continuous scroll, and you
 * reach the tree by dragging past the headings the way you would expect to. */
@media (max-width: 768px) {
  .rail-head .icon-btn { width: var(--tap); height: var(--tap); }
  #search-trigger { min-height: var(--tap); }
  .fy-row { min-height: 44px; }
  .dir { padding-top: 12px; padding-bottom: 3px; }
  .dir-toggle { min-height: 26px; }
  .outline-row { padding-top: 7px; padding-bottom: 7px; }
  .outline-fold { width: 34px; height: 30px; }
  #user-footer .icon-btn { width: var(--tap); height: var(--tap); }
}

#add-repo-btn, #new-doc-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: calc(100% - 8px);
  margin: 12px 4px 4px;
  padding: 9px;
  border: 1px dashed var(--border-dashed);
  background: transparent;
  border-radius: var(--r-md);
  color: var(--text-soft);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 550;
  cursor: pointer;
  transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
#add-repo-btn .ic, #new-doc-btn .ic { width: 13px; height: 13px; }
#add-repo-btn:hover, #new-doc-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-wash);
}
/* Starting a document is the action here; connecting a repository is upkeep. */
#new-doc-btn {
  border-style: solid;
  border-color: var(--accent-tint);
  background: var(--accent-wash);
  color: var(--accent);
  font-weight: 600;
}
#add-repo-btn { margin-top: 6px; }

#user-footer {
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  flex: none;
  padding: 11px 14px;
  border-top: 1px solid var(--border);
  background: var(--surface-footer);
}
#uf-pop {
  left: 14px;
  right: 14px;
  bottom: 50px;
  display: block;
}
.uf-note {
  display: block;
  padding: 8px 10px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text-soft);
}
.uf-id { min-width: 0; display: flex; flex-direction: column; }
.uf-name {
  font-size: 12.5px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.uf-role {
  font-size: 10.5px;
  color: var(--text-quiet);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#user-footer .icon-btn { margin-left: auto; width: 26px; height: 26px; }

/* ============================================================================
   Document
   ========================================================================= */

#doc-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
}

#doc-head {
  position: sticky;
  top: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  /* Sit over the column rather than over the pane. The breadcrumb names the
     document and the buttons act on it, but pinned to the pane's own edges
     they were floating 74px out to the left of the first word and 50px past
     the last — chrome belonging to the window, with a document happening
     somewhere inside it. Indented to the text edge instead, the bar reads as
     the top of the sheet. max() keeps the old padding whenever the pane is too
     narrow for a gutter to exist, and the phone layout overrides this outright
     further down. */
  padding: 13px max(28px, calc((100% - var(--measure)) / 2 + 24px));
  background: var(--veil);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border-soft);
  z-index: 3;
  flex: none;
}
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
  font-size: 12.5px;
  color: var(--text-soft);
  overflow: hidden;
  white-space: nowrap;
}
.breadcrumb .crumb { overflow: hidden; text-overflow: ellipsis; }
.breadcrumb .sep { color: var(--border-hover); flex: none; }
.breadcrumb .crumb.last { color: var(--text); font-weight: 600; flex: none; }
#doc-head #edit-btn { margin-left: auto; }
#doc-head #edit-btn .ic { width: 13.5px; height: 13.5px; }

/* "Running this document's scripts…" — an HTML document is rendered by running
   it in a browser on the server, and the reader has the un-rendered file until
   that lands. Sat next to the breadcrumb rather than over the article: it is a
   fact about the document, and the words underneath it are the ones being
   replaced. `flex: none` because the breadcrumb ellipses before this does —
   a truncated filename still names the file, a truncated chip says nothing. */
.snap-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 3px 10px 3px 8px;
  border: 1px solid var(--snap-border);
  border-radius: var(--r-pill);
  background: var(--snap-bg);
  color: var(--snap-text);
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
  animation: fade-up 0.2s ease;
}
.snap-chip::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--snap-dot);
  animation: snap-pulse 1.4s ease-in-out infinite;
}
@keyframes snap-pulse {
  50% { opacity: 0.25; }
}
@media (prefers-reduced-motion: reduce) {
  .snap-chip { animation: none; }
  .snap-chip::before { animation: none; }
}

/* The byline. Who wrote this, when, and how long it will take — the line a
   printed article carries above its first paragraph. Set in small letterspaced
   caps and ruled off, it stops reading as debug output about a file and starts
   reading as the masthead of something written. */
#doc-meta {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 30px 24px 0;
  width: 100%;
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}
#doc-meta .dot { color: var(--text-faint); margin: 0 6px; }
#doc-meta .open-count { color: var(--attn-text); font-weight: 600; }

.markdown-body {
  max-width: var(--measure);
  margin: 0 auto;
  width: 100%;
  padding: 44px 24px 120px;
  font-family: var(--font-doc);
  font-size: 19.5px;
  line-height: 1.72;
  color: var(--text-body);
  text-wrap: pretty;
  /* An identifier or URL with no break opportunity in it — a config key, a
     path — is otherwise wider than a phone, and one of them slides the whole
     page sideways. Code blocks are unaffected: `white-space: pre` ignores this,
     and they scroll on their own. */
  overflow-wrap: break-word;
  animation: fade-up 0.22s ease;
}
#doc-meta:not(.hidden) + .markdown-body { padding-top: 14px; }

@keyframes fade-up {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

.markdown-body h1 {
  font-size: 40px;
  line-height: 1.15;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  margin: 0 0 16px;
}
.markdown-body h2 {
  font-size: 25px;
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 40px 0 12px;
}
.markdown-body h3 {
  font-size: 20px;
  line-height: 1.3;
  font-weight: 600;
  color: var(--text);
  margin: 32px 0 10px;
}
.markdown-body h4, .markdown-body h5, .markdown-body h6 {
  font-size: 17px;
  font-weight: 650;
  color: var(--text);
  margin: 26px 0 8px;
}
/* An H2 opens a section, so it gets the space and the rule that says so. The
   old 40px top margin was the only thing separating one part of a long
   specification from the next, which on a document of twenty headings left the
   whole thing reading as one undifferentiated column. */
.markdown-body h2 {
  margin-top: 56px;
  padding-top: 22px;
  border-top: 1px solid var(--border-soft);
}
.markdown-body h1 + h2 { margin-top: 34px; border-top: 0; padding-top: 0; }

/* The drop cap. One letter, on the opening paragraph only, and the single
   clearest signal a page can give that what follows is meant to be read rather
   than scanned — which is the whole argument this product makes about
   documents. It sets in the document serif and takes the full ink colour, so
   it reads as lettering rather than as decoration. */
.markdown-body > h1:first-of-type + p::first-letter {
  float: left;
  margin: 0.02em 0.08em 0 0;
  font-size: 3.3em;
  line-height: 0.84;
  font-weight: 600;
  color: var(--text);
}

/* Headings inherit --font-doc along with the prose, deliberately. The document
   voice is the whole document, titles included — which is how the token is
   already used on the For-you and sign-in screens, where .needs-head h1 and
   .wp-left h1 ask for it by name. Setting article headings in the UI sans
   instead would leave this product's own H1 in a different face from every
   other page title in it. The rail's outline and the breadcrumb stay sans and
   should: an outline row is a pointer to a heading, not the heading. */
.markdown-body p { margin: 0 0 26px; }
.markdown-body ul, .markdown-body ol { margin: 0 0 26px; padding-left: 1.3em; }
.markdown-body li { margin-bottom: 8px; }
.markdown-body strong { font-weight: 650; color: var(--text); }
/* A section break the eye can rest on, rather than a hairline it skims past. */
.markdown-body hr {
  border: 0;
  height: 1px;
  margin: 52px auto;
  width: 44%;
  background: linear-gradient(to right, transparent, var(--border-hover), transparent);
}
.markdown-body a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  transition: border-color 0.12s ease;
}
.markdown-body a:hover { border-bottom-color: var(--accent); }

/* A link that points outside the portal, and one that points nowhere it can
   reach. Saying so beforehand beats a dead click. */
.markdown-body a.link-out::after {
  content: '↗';
  margin-left: 2px;
  font-size: 0.82em;
  vertical-align: baseline;
  opacity: 0.55;
}
.markdown-body a.link-dead {
  color: var(--text-soft);
  border-bottom-style: dotted;
  border-bottom-color: var(--border-hover);
  cursor: help;
}
.markdown-body a.link-dead:hover { border-bottom-color: var(--text-soft); }
.markdown-body code {
  font-family: var(--font-mono);
  font-size: 0.82em;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  padding: 0.1em 0.35em;
}
.markdown-body pre {
  position: relative;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 15px 17px;
  overflow-x: auto;
  margin: 0 0 26px;
}
.code-copy {
  position: absolute;
  top: 8px;
  right: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 7px);
  background: var(--card);
  color: var(--text-soft);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.code-copy .ic { width: 13px; height: 13px; }
.markdown-body pre:hover .code-copy, .code-copy:focus-visible { opacity: 1; }
.code-copy:hover { color: var(--text); border-color: var(--border-hover); }
.code-copy.ok { opacity: 1; color: var(--ok); border-color: var(--ok); }
@media (hover: none) {
  .code-copy { opacity: 1; } /* no hover to reveal it with */
}
.markdown-body pre code {
  background: none;
  border: 0;
  padding: 0;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--text-body);
}
/* A pull quote, not an indent. A quotation in a document is the one place the
   author stepped out of their own voice, and a 2px grey rule with italics at
   body size hid that instead of showing it. Larger, upright — italic loses
   legibility at this size — and marked in the same amber the highlights use,
   so a quote on the page and a quote on a comment card are visibly relatives. */
.markdown-body blockquote {
  margin: 34px 0;
  padding: 2px 0 2px 26px;
  border-left: 3px solid var(--hl-strong);
  color: var(--text-mid);
  font-size: 1.1em;
  line-height: 1.55;
  font-style: normal;
}
.markdown-body blockquote p:last-child { margin-bottom: 0; }
.markdown-body table {
  border-collapse: collapse;
  margin: 0 0 26px;
  font-family: var(--font-ui);
  font-size: 14px;
  display: block;
  overflow-x: auto;
}
.markdown-body th, .markdown-body td {
  border: 1px solid var(--border);
  padding: 8px 14px;
  text-align: left;
}
.markdown-body th {
  background: var(--surface-sunken);
  font-size: 12.5px;
  font-weight: 650;
  color: var(--text);
}
.markdown-body img { max-width: 100%; }

/* Code and tables reach into the gutter the column leaves — see --breakout.

   Rightward only. Every line of the page starts on the same vertical edge, and
   that edge is what the eye tracks down a document; a block that begins to the
   left of it does not read as "wider", it reads as misaligned, and it drags
   the start of the page sideways every time one appears. Growing from a fixed
   left edge keeps the spine intact and still gives a terminal transcript the
   room the prose measure was denying it.

   Direct children only: a block nested inside a list or a quote belongs to
   that list, and pulling it out sideways would break the indent that says so.
   Both keep their own horizontal scroll for the cases the gutter cannot
   cover, so nothing that used to be reachable stops being reachable. */
.markdown-body > pre,
.markdown-body > table {
  width: calc(100% + var(--breakout));
  max-width: calc(100% + var(--breakout));
  margin-right: calc(var(--breakout) * -1);
}

/* ---------- HTML documents ---------- */

/*
 * A document that arrived as HTML brings its own stylesheet, rewritten on the
 * server so that every selector in it starts at this class and can reach no
 * further. What that rewriting cannot do is stop the document restyling the
 * things the portal puts *inside* it — a highlight, a heading's link button —
 * because by then they are its descendants like anything else.
 *
 * So they are restated here, one specificity step above anything a scoped
 * sheet can produce on its own. A comment highlight is not decoration; it is
 * the only sign that a conversation exists on that sentence.
 */
/*
 * The article stops being a Markdown page when it is holding a styled HTML
 * document. `.markdown-body` comes off in applyDocTypography(), which takes
 * with it the 640px measure that used to sit *above* the document's own
 * wrapper and cap it, and the typography that used to fill in every property
 * the author had not thought to set. What is left is a place to put a page.
 */
#article.host-doc {
  width: 100%;
  padding: 40px 24px 120px;
  animation: fade-up 0.22s ease;
  /* What the document's own breakpoints now measure. The scoper turns its size
     media queries into container queries (see containerize), and this is the
     container they ask.
     On the article rather than on .html-doc, and that is not a detail: an
     element cannot query the container it *is*, and a document's stylesheet
     routinely says `@media (…) { body { … } }`, which scopes onto .html-doc
     itself. Put the container one level up and that rule works like any
     other. */
  container-type: inline-size;
}

/*
 * Containment, which is the portal's business even when the look is not.
 *
 * Taking `.markdown-body` off the article to let a document use its own
 * typography took these with it, and they were never typography: a table wider
 * than a phone, an image wider than its column and a code token with nowhere to
 * break are all the same failure, and the failure is not "the document looks
 * like its author made it" — it is the whole page sliding sideways under the
 * reader's thumb. Markdown documents never lost them, which is why this only
 * ever showed on HTML.
 *
 * Qualified with #article, and deliberately: a document may say what its table
 * looks like, and may not say that its table gets to move the page. Same
 * reasoning as the highlight rules further down, opposite to the width default
 * above, which a document is meant to win.
 */
/*
 * The boundary guard. Enumerating what might be too wide — tables, images,
 * code — was the wrong shape and did not hold: the document that prompted this
 * overflowed on an inline <code> carrying `white-space: nowrap`, where
 * overflow-wrap is inert by definition and overflow-x means nothing because the
 * box is inline. There will always be another one.
 *
 * So the guard moved to the one place that cannot be missed. Whatever a
 * document does inside itself, it scrolls itself and not the page. Height stays
 * auto, so this adds no vertical scroller — the pane keeps that job.
 */
#article.host-doc > .html-doc {
  overflow-x: auto;
}

/* Still worth having: these are the common cases, and containing them here
   means the scrollbar above appears only when something genuinely needs it. */
#article.host-doc table {
  display: block;
  overflow-x: auto;
  max-width: 100%;
}
#article.host-doc :is(img, video, svg, iframe) {
  max-width: 100%;
  height: auto;
}
#article.host-doc { overflow-wrap: break-word; }

.html-doc {
  /* Fixed positioning is demoted to absolute on the way in. This is what it
     is then measured against, so a pinned element lands inside the document
     rather than over the portal. */
  position: relative;

  /* The column, and no opinion about how much of it to use.
     There used to be a 78ch courtesy measure here for a document that named no
     width. It was wrong, and wrong in a way that only showed on the documents
     it was meant to help: a page that caps itself on an inner wrapper — the
     usual way a designed page is built — never sets a width on `body`, so it
     looked identical to a page that had said nothing, and got a *prose*
     measure. data-model.html asked for 1000px inside a 1148px column and was
     held at 655, with its sidebar and its inner wrap fighting over what was
     left and 493px of the pane empty beside it.
     A styled HTML document is a designed page. It gets the column, and decides
     for itself — which it can now do properly, because its own breakpoints
     finally measure that column. */
  max-width: 100%;
  margin-inline: auto;
}

#article .html-doc mark.mdc-hl {
  background: var(--hl);
  box-shadow: inset 0 -2px 0 var(--hl-strong);
  border-radius: 2px;
  padding: 1px 0;
  color: inherit;
  cursor: pointer;
  display: inline;
  visibility: visible;
  opacity: 1;
}
#article .html-doc mark.mdc-hl:hover { background: #FBE59B; }
#article .html-doc mark.mdc-hl.active { box-shadow: inset 0 -3px 0 var(--hl-strong); }
#article .html-doc mark.mdc-hl.resolved {
  background: var(--hl-resolved);
  box-shadow: inset 0 -2px 0 var(--ok);
}

#article .html-doc h2, #article .html-doc h3 { position: relative; }
#article .html-doc .h-anchor,
#article .html-doc .code-copy {
  position: absolute;
  display: inline-flex;
  border: 0;
  background: none;
  color: var(--text-faint);
  cursor: pointer;
}
#article .html-doc .h-anchor { top: 50%; left: -30px; transform: translateY(-50%); width: 24px; height: 24px; }
#article .html-doc .code-copy { top: 8px; right: 8px; width: 28px; height: 28px; background: var(--card); }

/* ---------- draw.io sketch canvas ---------- */

#drawio-modal {
  position: fixed;
  inset: 24px;
  z-index: 70;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border-radius: var(--r-xl);
  box-shadow: var(--e4), 0 0 0 1px var(--border);
  overflow: hidden;
}
#drawio-modal .dw-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-soft);
}
#drawio-modal .dw-title { flex: 1; font-size: 12.5px; font-weight: 600; color: var(--text-soft); }
#drawio-frame { flex: 1; width: 100%; border: 0; background: #fff; }
@media (max-width: 768px) {
  #drawio-modal { inset: 0; border-radius: 0; }
}

/* ---------- diagram studio ---------- */

#diagram-form { width: min(680px, calc(100vw - 32px)); }
.dg-ai { margin-bottom: 12px; }
.dg-ai-row { display: flex; gap: 8px; }
.dg-ai-row input { flex: 1; min-width: 0; }
.dg-cols {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 14px;
  margin-bottom: 6px;
}
/* Boxes beside Arrows needs about 600px. The dialog is ~341px wide on a phone,
   so the second column simply ran off the edge: you could add boxes and never
   reach the arrows that join them, which is most of a flowchart. Stacked, both
   columns get the full width. */
@media (max-width: 768px) {
  .dg-cols {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }
}
.dg-row {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 6px;
}
.dg-row input { flex: 1; min-width: 0; }
.dg-row select {
  flex: none;
  max-width: 110px;
  font-family: inherit;
  font-size: 12.5px;
  padding: 6px 6px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm, 7px);
  background: var(--card);
  color: var(--text);
}
.dg-row input[data-dg-elabel] { flex: 0 1 76px; }
.dg-arrow { color: var(--text-faint); flex: none; }
.dg-x {
  display: inline-flex;
  flex: none;
  border: 0;
  background: none;
  color: var(--text-faint);
  cursor: pointer;
  padding: 3px;
  border-radius: var(--r-xs);
}
.dg-x .ic { width: 11px; height: 11px; }
.dg-x:hover { color: var(--danger); background: var(--danger-bg); }
#dg-preview {
  min-height: 130px;
  max-height: 260px;
  overflow: auto;
  display: flex;
  justify-content: center;
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-md);
  background: var(--bg);
  padding: 10px;
  margin: 10px 0 4px;
}
#dg-advanced { margin: 8px 0 4px; }
#dg-advanced summary { font-size: 12px; color: var(--text-quiet); cursor: pointer; }
#dg-src {
  width: 100%;
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm, 7px);
  background: var(--card);
  color: var(--text);
  padding: 8px;
  resize: vertical;
}

/* Diagram cards inside the rich editor — a picture you click, never code. */
.rich-diagram {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  padding: 10px 12px;
  margin: 0 0 18px;
  cursor: pointer;
  transition: border-color 0.12s ease;
}
.rich-diagram:hover { border-color: var(--accent); }
.rich-diagram .rd-label {
  display: block;
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-quiet);
  margin-bottom: 6px;
}
.rich-diagram .rd-view { display: block; overflow-x: auto; }

/* ---------- mathematics ---------- */

/* A formula arrives from the server as MathML, which browsers lay out
   themselves — there is no stylesheet and no font file behind one, so all that
   is left to say here is where it sits on the page.

   The TeX it was written as is kept beside it, hidden, so a document can leave
   through the rich editor as the TeX it came in as. `#article` is restated for
   the same reason `.mermaid-src` restates it: a document carrying its own
   scoped stylesheet would otherwise outrank a bare class and print the source
   underneath its own rendering. */
.math-tex { display: none; }
#article .math-tex { display: none; }

/* Centred, and allowed its own scrollbar: a display formula does not wrap —
   a formula that wrapped would lie about where its terms group — so one longer
   than the measure has to be scrollable instead.

   `safe center` rather than plain centring, which is the difference between a
   scrollbar and a truncation: a centred box that overflows spills equally off
   both ends and the left end cannot be scrolled back to. `safe` gives up the
   centring exactly when it would cost the reader the start of the formula. */
.math-block {
  display: flex;
  justify-content: safe center;
  margin: 0 0 26px;
  overflow-x: auto;
}
.math-block math { font-size: 1.08em; }

/* And, like a code block or a table, a formula may use the gutter the prose
   measure leaves — see --breakout. Most of them then need no scrolling at all.

   Named by container rather than by `.markdown-body`, because --breakout is
   declared on #app and so resolves everywhere below it: the same rule written
   against the class would hand the gutter to a formula in the Ask pane, which
   has no gutter to take and would simply push it out of the pane. */
#article > .math-block,
#editor-preview > .math-block {
  width: calc(100% + var(--breakout));
  max-width: calc(100% + var(--breakout));
  margin-right: calc(var(--breakout) * -1);
}

/* TeX KaTeX could not parse. Shown as it was written rather than dropped — a
   formula the reader can retype beats a gap where one used to be. */
.math-tex-bad {
  font-family: var(--font-mono);
  font-size: 0.9em;
  color: var(--text-quiet);
}

/* ---------- mermaid diagrams ---------- */

/* The source fence stays in the DOM so comment anchors keep their offsets —
   the reader sees only the drawn figure. */
/* The source, once its picture exists. Not `pre.mermaid-src`: an HTML document
   may hold its diagram in a div, and that one would have stayed visible under
   the drawing. Qualified with #article as well, because a document's own
   stylesheet is scoped to a class and would otherwise outrank a bare one —
   what a diagram looks like is the author's, whether its source is shown twice
   is not. */
.mermaid-src { display: none; }
#article .mermaid-src { display: none; }
.mermaid-figure {
  margin: 0 0 26px;
  overflow-x: auto;
}

/* A diagram wrapped with zoom + fullscreen controls. The bar recedes until
   the reader hovers or tabs into the figure. */
.mermaid-wrap {
  position: relative;
  margin: 0 0 26px;
}
.mermaid-wrap .mermaid-figure { margin: 0; }

.mermaid-toolbar {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  display: flex;
  gap: 2px;
  padding: 3px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.mermaid-wrap:hover .mermaid-toolbar,
.mermaid-wrap:focus-within .mermaid-toolbar,
.mermaid-wrap.is-full .mermaid-toolbar {
  opacity: 1;
}
@media (hover: none) {
  .mermaid-toolbar { opacity: 1; }
}

.mmd-btn {
  min-width: 30px;
  height: 30px;
  padding: 0 7px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.mmd-btn:hover { background: var(--surface-hover-card); }
.mmd-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Drawn, not written — see addMermaidControls. A text node here would join the
   article's text, and the article's text is what comment quotes are taken from. */
.mmd-btn[data-act="out"]::before { content: "\2212"; }
.mmd-btn[data-act="in"]::before { content: "+"; }
.mmd-level::before { content: attr(data-level); }

.mmd-level {
  min-width: 48px;
  font-size: 12px;
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}

/* Fullscreen: fill the screen, centre the diagram, let it scroll when zoomed. */
.mermaid-wrap.is-full {
  background: var(--bg);
  padding: 24px;
  display: flex;
  align-items: safe center;
  justify-content: safe center;
  overflow: auto;
}
.mermaid-wrap.is-full .mermaid-figure { overflow: visible; }

/* ---------- highlights ---------- */

mark.mdc-hl {
  background: var(--hl);
  box-shadow: inset 0 -2px 0 var(--hl-strong);
  border-radius: 2px;
  padding: 1px 0;
  color: inherit;
  cursor: pointer;
  transition: background 0.15s ease;
}
mark.mdc-hl:hover { background: #FBE59B; }
mark.mdc-hl.active { box-shadow: inset 0 -3px 0 var(--hl-strong); }
mark.mdc-hl.resolved {
  background: var(--hl-resolved);
  box-shadow: inset 0 -2px 0 var(--ok);
}
mark.mdc-hl.pulse { animation: hl-pulse 1.1s ease; }
@keyframes hl-pulse {
  0% { box-shadow: inset 0 -3px 0 var(--hl-strong), 0 0 0 0 var(--hl-strong); }
  70% { box-shadow: inset 0 -3px 0 var(--hl-strong), 0 0 0 9px transparent; }
  100% { box-shadow: inset 0 -3px 0 var(--hl-strong), 0 0 0 0 transparent; }
}

/* ---------- section links ---------- */

.markdown-body h2, .markdown-body h3 { position: relative; }
.h-anchor {
  position: absolute;
  top: 50%;
  left: -30px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border: 0;
  border-radius: var(--r-xs);
  background: none;
  color: var(--text-faint);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, color 0.12s ease;
}
.h-anchor .ic { width: 13px; height: 13px; }
.markdown-body h2:hover .h-anchor, .markdown-body h3:hover .h-anchor,
.h-anchor:focus-visible { opacity: 1; }
.h-anchor:hover { color: var(--accent); background: var(--surface-hover); }
@media (hover: none) {
  .h-anchor { display: none; } /* nothing to hover with, and no gutter to sit in */
}

/* ---------- margin notes (conversation chips in the article gutter) ---------- */

#doc-pane { position: relative; }
#margin-layer {
  position: absolute;
  z-index: 2;
  pointer-events: none;
}
.margin-note {
  position: absolute;
  left: calc(100% - 6px);
  display: inline-flex;
  align-items: center;
  gap: 3px;
  height: 26px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--card);
  box-shadow: var(--e1);
  font-family: var(--font-ui);
  cursor: pointer;
  pointer-events: auto;
  transition: transform 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}
.margin-note .avatar.sm { width: 20px; height: 20px; font-size: 8.5px; }
.margin-note .mn-count {
  font-size: 10.5px;
  font-weight: 650;
  color: var(--text-soft);
  padding-right: 5px;
}
.margin-note:hover { transform: translateX(-3px); border-color: var(--border-hover); }
.margin-note.active {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 1px 3px rgba(25, 24, 23, 0.1);
}
.margin-note.resolved { opacity: 0.55; }

/* ---------- avatars ---------- */

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--av-2);
  color: var(--on-av);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.3px;
  flex: none;
}
.avatar.sm { width: 24px; height: 24px; font-size: 9.5px; }

/* ============================================================================
   First run — an empty portal that asks for exactly one thing
   ========================================================================= */

#app.welcome-view { grid-template-columns: minmax(0, 1fr); }
#app.welcome-view #files-pane,
#app.welcome-view #doc-pane,
#app.welcome-view #needs-pane,
#app.welcome-view #comments-pane { display: none; }

/* Signed out: explain the thing before asking anyone to authenticate. */
#app.signin-view { grid-template-columns: minmax(0, 1fr); }
#app.signin-view #files-pane,
#app.signin-view #doc-pane,
#app.signin-view #needs-pane,
#app.signin-view #welcome-pane,
#app.signin-view #comments-pane { display: none; }

#signin-pane,
#welcome-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
  background: var(--bg);
}

/* An anchor, not a button — it is a real navigation to /auth/login, so it
   works without JavaScript and the server can set its transaction cookie. */
.cta-primary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 11px 18px;
  border-radius: var(--r-lg);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  box-shadow: 0 4px 14px rgba(79, 70, 214, 0.28);
  transition: filter 0.12s ease;
}
.cta-primary:hover { filter: brightness(1.08); }
.ms-mark {
  display: inline-grid;
  grid-template-columns: 8px 8px;
  grid-template-rows: 8px 8px;
  gap: 2px;
  flex: none;
}
.ms-mark i { display: block; }

/* Second and later providers: same shape, no fill. One primary action per
   screen, so the choice reads as a choice rather than a ranking. */
.cta-secondary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 11px 18px;
  border-radius: var(--r-lg);
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--border);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.12s ease;
}
.cta-secondary:hover { background: var(--bg-soft, var(--card)); filter: brightness(0.98); }

#signin-options { display: flex; flex-wrap: wrap; gap: 10px; }
.si-provider { min-width: 13rem; justify-content: center; box-shadow: var(--e1); }
/* A white disc under the Google mark: their guidance is that it keeps its
   colours on any surface, which a dark theme would otherwise fight. */
.g-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #fff;
  flex: none;
}

.si-email { margin: 18px 0 0; max-width: 26rem; }
/* With a provider button above, this is the alternative — the rule and the
   word carry that, so the two paths do not compete for the same emphasis. */
.si-email-alt { margin-top: 22px; padding-top: 20px; position: relative; }
.si-email-alt::before {
  content: "or";
  position: absolute;
  top: -8px;
  left: 0;
  padding-right: 10px;
  background: var(--bg);
  font-size: 12px;
  color: var(--text-soft);
}
.si-email-alt::after {
  content: "";
  position: absolute;
  top: 0;
  left: 1.9rem;
  right: 0;
  height: 1px;
  background: var(--border);
}
.si-email-label {
  display: block;
  margin-bottom: 6px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-mid);
}
.si-email-row { display: flex; gap: 8px; flex-wrap: wrap; }
.si-email-row input {
  flex: 1 1 12rem;
  min-width: 0;
  padding: 11px 13px;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}
.si-email-row input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: transparent;
}
.si-email-go { border: 0; cursor: pointer; font-family: inherit; }
.si-email-go:disabled { opacity: 0.6; cursor: default; filter: none; }
.si-sent {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}
.si-sent svg { flex: none; color: var(--ok, var(--accent)); }
.si-note { margin: 14px 0 0; font-size: 12.5px; color: var(--text-soft); }
.si-error { color: var(--danger, #c0392b); font-weight: 500; }

.si-card {
  background: var(--card);
  border-radius: var(--r-2xl);
  padding: 22px;
  box-shadow: var(--e4), 0 0 0 1px var(--border);
}
.si-card h2 { margin: 0 0 12px; font-size: 15px; font-weight: 650; letter-spacing: -0.01em; }
.si-card ul { margin: 0; padding: 0; list-style: none; }
.si-card li {
  position: relative;
  padding: 0 0 12px 20px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-mid);
}
.si-card li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 7px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}
.si-private {
  margin: 6px 0 0;
  padding: 10px 12px;
  border-radius: var(--r-md);
  background: var(--attn-bg);
  font-size: 12px;
  line-height: 1.5;
  color: var(--attn-text);
}
.wp-bar {
  display: flex;
  align-items: center;
  gap: 9px;
  flex: none;
  padding: 14px 24px;
  border-bottom: 1px solid var(--border-soft);
}
.wp-bar .spacer { flex: 1; }
.wp-ws { font-size: 13.5px; font-weight: 650; letter-spacing: -0.01em; }
.wp-user { font-size: 12.5px; color: var(--text-soft); }
.wp-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 420px;
  gap: 56px;
  padding: 62px 60px;
  align-items: start;
}
.wp-left { max-width: 430px; }
.wp-left h1 {
  font-family: var(--font-doc);
  font-size: 38px;
  line-height: 1.15;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  margin: 0 0 18px;
}
.wp-lede { margin: 0 0 26px; font-size: 15px; line-height: 1.65; color: var(--text-mid); }
.wp-steps { list-style: none; margin: 0 0 30px; padding: 0; }
.wp-steps li {
  display: flex;
  gap: 12px;
  margin-bottom: 14px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--text-mid);
}
.wp-steps b { font-weight: 650; color: var(--text); }
.wp-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex: none;
  border-radius: var(--r-sm);
  background: var(--attn-bg);
  color: var(--attn-text);
  font-size: 12px;
  font-weight: 700;
}
/* The way past the first-run screen. Deliberately quiet next to the CTA — most
   people should connect a repository, but nobody should be stuck here. */
.wp-skip-line {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-mid);
  max-width: 34rem;
}
.wp-skip {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--accent);
  font-weight: 550;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.wp-skip:hover { text-decoration-thickness: 2px; }

.wp-cta {
  padding: 11px 18px;
  border-radius: var(--r-lg);
  font-size: 14px;
  box-shadow: 0 4px 14px rgba(79, 70, 214, 0.28);
}
.wp-right { min-width: 0; }

/* The connect dialog moves in here on first run instead of floating. */
#repo-form.inline {
  position: static;
  transform: none;
  width: 100%;
  animation: none;
}
#repo-form.inline #repo-cancel { display: none; }
#repo-form.inline .row { justify-content: stretch; }
#repo-form.inline .row .primary { flex: 1; justify-content: center; }
.repo-hint {
  margin: -7px 0 13px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-soft);
}
.repo-hint code { font-family: var(--font-mono); font-size: 0.92em; color: var(--text-mid); }
/* The way out of a failed private clone. A link rather than a button because
   it leaves the page — but weighted like the primary action, since it is. */
.repo-gh-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
  padding: 8px 14px;
  border-radius: var(--r-md);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
}
.repo-gh-btn:hover { filter: brightness(1.06); }
.repo-gh-btn .ic { width: 15px; height: 15px; }
.repo-gh-note {
  display: block;
  margin-top: 7px;
  font-size: 12px;
  color: var(--text-soft);
}
.repo-hint.bad { color: var(--danger); }
.derived-pill {
  background: var(--surface-sunken);
  color: var(--text-soft);
  border-radius: var(--r-xs);
  padding: 1px 5px;
  font-size: 11px;
  font-weight: 600;
  margin-left: 4px;
}

/* ============================================================================
   Presence
   ========================================================================= */

.presence {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 3px 8px 3px 4px;
  border-radius: var(--r-pill);
  background: var(--surface-footer);
  animation: fade-up 0.2s ease;
}
.presence .faces { display: inline-flex; }
.presence .avatar {
  width: 21px;
  height: 21px;
  font-size: 8.5px;
  box-shadow: 0 0 0 1.5px var(--bg);
}
.presence .avatar + .avatar { margin-left: -6px; }
.presence .p-label { font-size: 11.5px; color: var(--text-quiet); white-space: nowrap; }
.presence { cursor: pointer; border: 0; transition: background 0.12s ease; }
.presence:hover { background: var(--surface-hover-card); }
.presence:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.presence .p-icon { display: inline-flex; color: var(--text-quiet); }
.presence .p-icon .ic { width: 15px; height: 15px; }
.presence .p-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 21px;
  height: 21px;
  padding: 0 4px;
  margin-left: -6px;
  border-radius: 50%;
  background: var(--surface-sunken);
  box-shadow: 0 0 0 1.5px var(--bg);
  font-size: 9px;
  font-weight: 700;
  color: var(--text-quiet);
}

/* Who's here, and where — the presence popover. */
.presence-pop {
  position: absolute;
  z-index: 20;
  width: 268px;
  max-width: calc(100vw - 32px);
  padding: 6px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2, var(--e3));
  animation: fade-up 0.14s ease;
}
.pp-sec { padding: 4px; }
.pp-sec + .pp-sec { border-top: 1px solid var(--border-soft); margin-top: 2px; }
.pp-sec-title {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  padding: 2px 4px 6px;
}
.pp-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px;
  border-radius: var(--r-sm);
}
.pp-name { font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-empty { font-size: 12.5px; color: var(--text-quiet); padding: 2px 4px 4px; }
.pp-file {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 4px;
  border-radius: var(--r-sm);
  text-decoration: none;
  color: inherit;
}
.pp-file:hover { background: var(--surface-hover-card); }
.pp-file-people { display: inline-flex; flex: none; }
.pp-file-people .avatar + .avatar { margin-left: -6px; }
.pp-file-info { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.pp-file-path {
  font-size: 13px;
  font-weight: 550;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pp-file-repo { font-size: 10.5px; color: var(--text-faint); }

/* ============================================================================
   Needs you (cross-repo attention list)
   ========================================================================= */

/* With the needs list mounted there is no document, so no comments pane. */
#app.needs-view { grid-template-columns: var(--rail-w) minmax(0, 1fr); }
#app.pane-closed, #app.needs-view { --pane-now: 0px; }

#app.needs-view #comments-pane, #app.needs-view #doc-pane { display: none; }

#needs-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
  background: var(--bg);
}
.needs-head { padding: 30px max(44px, (100% - var(--page-max)) / 2) 0; flex: none; }
.needs-head #needs-back { margin-bottom: 18px; }
.needs-head h1 {
  font-family: var(--font-doc);
  font-size: 30px;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--text);
  margin: 0 0 8px;
}
/* The deck — the line a magazine sets under a headline to say what the piece
   is before you commit to it. It was body-size UI grey; in the document's
   voice, a size up and with room around it, it reads as part of the page
   rather than as a caption bolted under the title. */
.needs-head p {
  margin: 0 0 26px;
  font-family: var(--font-doc);
  font-size: 17px;
  line-height: 1.55;
  color: var(--text-quiet);
  max-width: 60ch;
}
.needs-filters {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--border-soft);
  flex-wrap: wrap;
}
/* The chip row takes the whole line once it wraps, so the sort control drops
   below it rather than being stranded beside a two-row block of chips. */
.needs-filters .chips { padding: 0; flex: 1 1 auto; min-width: min(100%, 420px); }
.needs-filters #needs-sort { margin-left: auto; flex: none; }

/* The needs list is a page, not a pane: it needs its own breathing room to
   collapse before the mobile breakpoint, since it can be shown in a narrow
   side-by-side window well above 768px. */
@media (max-width: 1100px) {
  .needs-head { padding: 22px 24px 0; }
  .needs-head h1 { font-size: 25px; }
  #needs-list { padding: 16px 24px 40px; }
  .ni-actions .reply-input { min-width: 100%; }
}

#needs-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px max(44px, (100% - var(--page-max)) / 2) 40px;
}
#needs-list > * { flex: none; }
/* The file a run of items belongs to. It was the faintest thing on the screen
   while being the only thing that said which document you were answering
   about, and it scrolled away, so by the third item you no longer knew. It now
   holds the top of the pane while its own items pass under it, and carries how
   many of them there are — the number that tells you whether this file is one
   quick reply or an afternoon. */
.needs-group {
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 22px 2px 9px;
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg);
  /* Each file opens a section and gets the rule an H2 gets in the document.
     On a pseudo-element rather than border-top so it spans the full width
     regardless of the header's own padding; it is part of the sticky box, so
     it caps the header while that header is pinned. */
}
.needs-group::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 1px;
  background: var(--border-soft);
}
#needs-list > .needs-group:first-child::before { display: none; }
.needs-group .ng-repo { font-size: 12px; font-weight: 650; color: var(--text); }
.needs-group .ng-path { font-size: 12.5px; font-weight: 600; color: var(--text-mid); }
.needs-group .ng-count {
  margin-left: auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  flex: none;
}

.needs-item {
  display: grid;
  grid-template-columns: 3px 1fr;
  border-radius: var(--r-lg);
  box-shadow: 0 0 0 1px var(--border), var(--e1);
  overflow: hidden;
  background: var(--card);
}
/* The spine repeats the pill's level in the margin, where it can be scanned
   down the page without reading a word — the fast pass that tells you whether
   there is anything here blocking somebody before you start at the top. It was
   amber for every row, which said only "this is a row". */
.needs-item .spine { background: var(--border-strong); }
.needs-item[data-tone='ask'] .spine { background: var(--hl-strong); }
.needs-item[data-tone='note'] .spine,
.needs-item.mine .spine { background: var(--accent); }
.needs-item[data-tone='gone'] .spine { background: var(--danger); }

/* Answered-this-visit: present but visibly settled, so it reads as "done, but
   still here if you want it" rather than a fresh demand. */
.needs-group.replied-group { opacity: 0.85; }
.needs-item.replied { background: var(--surface-hover-card); box-shadow: 0 0 0 1px var(--border); }
.needs-item.replied .spine { background: var(--sync-dot); }
.ni-body { padding: 14px 16px; min-width: 0; }
.ni-head { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; flex-wrap: wrap; }
.ni-head .ni-name { font-size: 13px; font-weight: 650; color: var(--text); }
.ni-age { margin-left: auto; font-size: 11.5px; color: var(--text-faint); flex: none; }
.reason-pill {
  font-size: 10.5px;
  font-weight: 650;
  border-radius: var(--r-xs);
  padding: 2px 6px;
  background: var(--surface-sunken);
  color: var(--text-quiet);
}
/* Three levels of "why is this here", so the amber means something again. A
   question is somebody blocked on you and keeps the attention colour; a
   mention or a reply was addressed to you personally and takes the quieter
   accent; a new comment on a document you follow is ambient and sits on plain
   surface. Every one of them used to be amber, which is the same as none. */
.reason-pill.ask { background: var(--attn-chip); color: var(--attn-chip-text); }
.reason-pill.note, .reason-pill.mine { background: var(--accent-wash); color: var(--accent); }
.reason-pill.gone { background: var(--danger-bg); color: var(--danger); }
.ni-quote {
  margin: 0 0 10px;
  padding-left: 9px;
  border-left: 2px solid var(--hl-strong);
  font-family: var(--font-doc);
  font-size: 14px;
  line-height: 1.5;
  font-style: italic;
  color: var(--text-quiet);
}
.ni-quote.gone { border-left-color: var(--danger); }
.ni-quote-tag {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-family: var(--font-ui, inherit);
  font-style: normal;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--danger);
  vertical-align: middle;
}
.ni-quote-tag .ic { width: 11px; height: 11px; }
.ni-text {
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-body);
  white-space: pre-wrap;
  margin: 0 0 12px;
  word-wrap: break-word;
}
.ni-replies {
  margin: -4px 0 12px;
  padding-left: 12px;
  border-left: 1.5px solid var(--border-soft);
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-quiet);
}
.ni-replies b { color: var(--text-mid); font-weight: 650; }
.ni-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ni-actions .reply-input { flex: 1; min-width: 140px; }
.ni-actions .primary { padding: 6px 13px; font-size: 12.5px; }
/* Answering here is the whole point of the screen; opening the document is the
   way out of it. Bordered and shoulder to shoulder with Reply, the two read as
   a choice between equals, and the row ended in two buttons competing for the
   same glance. Resting borderless settles that without moving or hiding it —
   the same demotion the tracker's export buttons take. */
.ni-actions .ghost-btn { border-color: transparent; background: none; color: var(--text-quiet); }
.ni-actions .ghost-btn:hover { border-color: var(--accent); background: var(--card); color: var(--accent); }

.needs-tail {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 13px 16px;
  border-radius: var(--r-lg);
  background: var(--ok-bg);
  border: 1px solid var(--ok-border);
  font-size: 13px;
  color: var(--text-mid);
}
.needs-tail .ic { width: 16px; height: 16px; color: var(--ok); }

/* ---- "Assigned to you" chip: task rows, not thread rows. Deliberately
   plainer than .needs-item — a task has no author, no quote, no reply box,
   so it does not earn that card's weight — closer in spirit to
   .needs-approval's one-line "here's a thing, go open it" shape. ---- */
.assigned-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  margin: 0 0 8px;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--card);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.assigned-row:hover { background: var(--surface-hover-card); }
.assigned-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.assigned-row.overdue { border-color: color-mix(in srgb, var(--data-red) 45%, transparent); }
.ar-main { display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1; }
.ar-title {
  font-size: 13.5px;
  font-weight: 650;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ar-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--text-quiet); }
.ar-repo { font-weight: 600; color: var(--text-mid); }
.ar-due.overdue { color: var(--data-red-ink); font-weight: 650; }
.assigned-row .tk-avatars { flex: none; }
/* Read-only echoes of the tracker's own status pill — not a <select>, so the
   padding tightens to match a plain <span>'s baseline. */
.assigned-row .tk-status { padding: 0.18rem 0.35rem; cursor: default; }

/* ============================================================================
   Comments pane
   ========================================================================= */

#comments-pane {
  position: relative;
  background: var(--bg-soft);
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Drag the pane wider — room for a diagram — or narrower to read. */
#pane-resize {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -3px;
  width: 7px;
  z-index: 15;
  cursor: col-resize;
  background: transparent;
  transition: background 0.12s ease;
}
#pane-resize::after {
  content: '';
  position: absolute;
  inset: 0 3px;
  background: var(--accent);
  opacity: 0;
  transition: opacity 0.12s ease;
}
#pane-resize:hover::after, #pane-resize.dragging::after, #pane-resize:focus-visible::after { opacity: 0.55; }
#pane-resize:focus-visible { outline: none; }
/* While dragging, nothing else should take the pointer or select text. */
body.resizing-pane { user-select: none; cursor: col-resize; }
body.resizing-pane iframe, body.resizing-pane #article { pointer-events: none; }
@media (max-width: 1279px) {
  #pane-resize { display: none; }
}

.pane-sticky {
  flex: none;
  background: var(--bg-soft);
  border-bottom: 1px solid var(--border);
}
.pane-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 15px 16px 11px;
}
/* The standing label. This exact recipe — ~10.5px, 700, 0.09em, uppercase,
   --text-faint — was already in the rail (.rail-label), on the analytics
   section headings and, since the document got its byline, over every article.
   "Discussion" was the odd one out at 14px sentence case, so a pane label and a
   rail label looked like two different kinds of thing. They are the same kind
   of thing: a name for the region under it. */
.pane-title {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.count { font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; color: var(--text-faint); }

.chips { display: flex; gap: 5px; padding: 0 16px 12px; flex-wrap: wrap; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 1px solid var(--border-strong);
  background: transparent;
  border-radius: var(--r-pill);
  padding: 5px 10px;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--text-quiet);
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease;
}
.chip b { font-weight: 650; color: var(--text-faint); }
.chip b:empty { display: none; }
.chip:hover { border-color: var(--border-hover); color: var(--text); }
.chip.active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--ink-text);
  font-size: 12px;
  font-weight: 600;
}
.chip.active b { color: var(--ink-accent); }

#threads {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 4px 12px 16px;
}
#threads .file-head {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 12px 4px 2px;
  font-size: 12px;
  font-weight: 650;
  color: var(--text-quiet);
  cursor: pointer;
  background: none;
  border: 0;
  text-align: left;
  font-family: inherit;
}
#threads .file-head .ic { width: 13px; height: 13px; color: var(--text-faint); }
#threads .file-head:hover { color: var(--accent); }

/* ---------- Ask (assistant) ---------- */

/* With the assistant on, the tabs are the pane's header. */
#comments-pane.has-chat .pane-title, #comments-pane.has-chat #comments-count { display: none; }
#pane-tabs { margin: -2px 0; }

#chat-panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#chat-bar {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
}
#chat-picker {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border-strong);
  background: var(--card);
  border-radius: var(--r-md);
  padding: 5px 9px;
  font-family: inherit;
  font-size: 12.5px;
  color: var(--text-mid);
  cursor: pointer;
  text-align: left;
}
#chat-picker:hover { border-color: var(--border-hover); color: var(--text); }
#chat-picker-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-chev {
  flex: none;
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid var(--text-faint);
}
#chat-new .ic { width: 15px; height: 15px; }

#chat-menu {
  position: absolute;
  top: calc(100% - 2px);
  left: 12px;
  right: 12px;
  z-index: 20;
  max-height: 320px;
  overflow-y: auto;
  background: var(--card);
  border-radius: var(--r-md);
  box-shadow: var(--e3), 0 0 0 1px var(--border);
  padding: 5px;
}
.cm-head {
  padding: 7px 8px 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.cm-none { padding: 2px 8px 8px; font-size: 12px; color: var(--text-faint); }
.cm-item {
  position: relative;
  display: block;
  width: 100%;
  border: 0;
  background: none;
  border-radius: var(--r-sm, 7px);
  padding: 7px 26px 7px 8px;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.cm-item:hover { background: var(--surface-hover); }
.cm-item.on { background: var(--accent-wash); }
.cm-title {
  display: block;
  font-size: 12.5px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cm-item.on .cm-title { color: var(--accent); font-weight: 600; }
.cm-meta { display: block; font-size: 11px; color: var(--text-faint); margin-top: 1px; }
.cm-del {
  position: absolute;
  top: 50%;
  right: 5px;
  transform: translateY(-50%);
  display: inline-flex;
  padding: 4px;
  border-radius: var(--r-xs);
  color: var(--text-faint);
  opacity: 0;
}
.cm-del .ic { width: 12px; height: 12px; }
.cm-item:hover .cm-del { opacity: 1; }
.cm-del:hover { color: var(--danger); background: var(--danger-bg); }

#chat-msgs {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 16px;
}
.chat-hello {
  margin: auto 0;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 20px 10px;
  color: var(--text-soft);
}
.chat-hello .ch-icon { color: var(--text-faint); }
.chat-hello .ch-icon .ic { width: 26px; height: 26px; }
.chat-hello strong { font-size: 13.5px; color: var(--text); }
.chat-hello p { margin: 0 0 8px; font-size: 12.5px; line-height: 1.55; max-width: 320px; }
.chat-suggest {
  border: 1px solid var(--border-strong);
  background: var(--card);
  border-radius: var(--r-pill);
  padding: 7px 13px;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--text-mid);
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease;
}
.chat-suggest:hover { border-color: var(--accent); color: var(--accent); }
.chat-clear {
  align-self: flex-start;
  flex: none;
}
.chat-msg.user { display: flex; justify-content: flex-end; }
.chat-msg.user .chat-bubble {
  max-width: 85%;
  background: var(--ink);
  color: var(--ink-text);
  border-radius: 14px 14px 4px 14px;
  padding: 8px 12px;
  font-size: 13px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
.chat-msg.assistant { display: flex; flex-direction: column; gap: 6px; }
.chat-tool {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  color: var(--text-quiet);
}
.chat-tool .ic { width: 12px; height: 12px; flex: none; color: var(--text-faint); }
.chat-body {
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-body);
}
.chat-body.plain { white-space: pre-wrap; overflow-wrap: break-word; }
/* The assistant's answers reuse the document styles, scaled to the pane. */
#chat-msgs .markdown-body {
  padding: 0;
  margin: 0;
  max-width: none;
  font-family: var(--font-ui);
  font-size: 13px;
  line-height: 1.6;
  animation: none;
}
#chat-msgs .markdown-body h1, #chat-msgs .markdown-body h2, #chat-msgs .markdown-body h3 {
  font-size: 13.5px;
  margin: 12px 0 6px;
}
#chat-msgs .markdown-body p, #chat-msgs .markdown-body ul, #chat-msgs .markdown-body ol,
#chat-msgs .markdown-body pre, #chat-msgs .markdown-body table { margin: 0 0 10px; }
#chat-msgs .markdown-body pre { padding: 10px 12px; }
#chat-msgs .math-block { margin: 0 0 10px; }

/* The drop cap is a reading affordance for the opening paragraph of a
   document, and an answer in this pane is not a document — it is a reply, and
   it has no opening paragraph. Left to fire here it landed on whatever the
   model's first heading happened to be followed by, which in the answer that
   found this was the `\t` of a mangled `\text{}` set in 3.3em serif. */
#chat-msgs .markdown-body > h1:first-of-type + p::first-letter {
  float: none;
  margin: 0;
  font-size: inherit;
  line-height: inherit;
  font-weight: inherit;
  color: inherit;
}

#chat-msgs .markdown-body pre code { font-size: 11.5px; }
#chat-msgs .markdown-body code { font-size: 0.9em; }
.chat-vote { display: flex; align-items: center; gap: 4px; font-size: 11.5px; color: var(--text-quiet); }
.chat-vote .spacer { flex: 1; }
.cv-noted { color: var(--text-faint); }
.cv-doc {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: var(--r-pill);
  padding: 3px 9px 3px 7px;
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-quiet);
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease;
}
.cv-doc .ic { width: 12px; height: 12px; }
.cv-doc:hover { border-color: var(--accent); color: var(--accent); }

.nd-seed-note {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin: 0 0 16px;
  padding: 9px 11px;
  border-radius: var(--r-md);
  background: var(--accent-wash);
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-mid);
}
.nd-seed-note .ic { width: 14px; height: 14px; flex: none; color: var(--accent); margin-top: 1px; }
.cv-btn {
  border: 0;
  background: none;
  cursor: pointer;
  font-size: 13px;
  padding: 2px 4px;
  border-radius: var(--r-xs);
  opacity: 0.55;
}
.cv-btn:hover { opacity: 1; background: var(--surface-hover); }
.chat-cursor {
  display: inline-block;
  width: 7px;
  height: 14px;
  vertical-align: -2px;
  background: var(--accent);
  border-radius: 2px;
  animation: chat-blink 1s steps(2) infinite;
}
@keyframes chat-blink { 50% { opacity: 0; } }

#chat-form {
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 10px 12px calc(12px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border);
  background: var(--bg-soft);
}
#chat-input {
  flex: 1;
  min-width: 0;
  resize: none;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  line-height: 1.5;
  padding: 8px 10px;
}
#chat-input:focus { outline: none; border-color: var(--accent); }
#chat-send { flex: none; color: var(--accent); }

/* ---------- thread card ---------- */

.thread {
  flex: none;
  background: var(--card);
  border-radius: var(--r-xl);
  box-shadow: 0 0 0 1px var(--border), var(--e1);
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow 0.15s ease;
  animation: fade-up 0.2s ease;
}
.thread:hover { box-shadow: 0 0 0 1px var(--border-hover), var(--e1); }
.thread.active { box-shadow: 0 0 0 1.5px var(--accent), 0 4px 14px rgba(79, 70, 214, 0.12); }
.thread.needs-you { box-shadow: 0 0 0 1.5px var(--accent), 0 4px 14px rgba(79, 70, 214, 0.12); }
.thread.resolved { opacity: 0.72; }

.attn-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 13px;
  background: var(--attn-bg);
  border-bottom: 1px solid var(--attn-border);
}
.attn-banner .at-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex: none;
  border-radius: var(--r-xs);
  background: var(--attn-chip);
  color: var(--attn-chip-text);
  font-size: 10px;
  font-weight: 800;
}
.attn-banner .at-text {
  flex: 1;
  min-width: 0;
  font-size: 11.5px;
  font-weight: 650;
  color: var(--attn-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attn-banner .at-time { font-size: 11px; color: var(--attn-text-quiet); flex: none; }

.thread-body { padding: 11px 13px 10px; }

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px 2px 6px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  font-size: 10.5px;
  font-weight: 650;
  color: var(--text-quiet);
  margin-bottom: 8px;
}
.status-pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-faint); flex: none; }
.status-pill .dot.waiting { background: var(--warn-dot); }
.status-pill .dot.answered { background: var(--ok); }
.status-pill .dot.decision { background: var(--accent); }
/* A question that became work: not the amber that means "waiting on you" —
   that dot is deliberately the only saturated one in this set — and not the
   green that means "finished". Same quiet grey .ticket-note already uses for
   its own "in progress" text, so the pill and the card agree. */
.status-pill .dot.tracked { background: var(--text-quiet); }
.status-pill.gone {
  background: var(--danger-bg);
  color: var(--danger);
}
.status-pill.gone .ic { width: 11px; height: 11px; }

.quote {
  margin: 0 0 10px;
  padding-left: 8px;
  border-left: 2px solid var(--hl-strong);
  font-family: var(--font-doc);
  font-size: 13px;
  line-height: 1.5;
  font-style: italic;
  color: var(--text-quiet);
  overflow: hidden;
}
/* The quote doubles as the keyboard route to the highlighted passage. */
button.quote-btn {
  display: block;
  width: 100%;
  text-align: left;
  font-family: var(--font-doc);
  background: none;
  border: 0;
  border-left: 2px solid var(--hl-strong);
  cursor: pointer;
}
button.quote-btn:hover { color: var(--text-body); }
.thread.gone .quote {
  border-left-color: var(--border-strong);
  color: var(--text-faint);
  text-decoration: line-through;
}
/* Lost-anchor explainer: a plain-language line above the text the orphaned
   comment was written about, so the reader knows what it referred to. */
/* This block is context for the comment, not the comment. It arrives first,
   because the reader has to know the anchor is gone before the remark makes
   sense — but set in danger red at two lines tall above a one-line remark, the
   loudest thing on the card was a note about the card. Gathering it onto one
   sunken panel and moving the red into the icon keeps the warning legible —
   the quote inside is still struck through — while the comment below it is
   what the eye lands on. */
.quote-lost {
  margin-bottom: 12px;
  padding: 7px 9px;
  border-radius: var(--r-md);
  background: var(--surface-sunken);
}
.quote-lost .quote { margin-bottom: 0; font-size: 12.5px; }
.quote-lost-label {
  display: flex;
  align-items: flex-start;
  gap: 5px;
  margin-bottom: 5px;
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--text-quiet);
}
.quote-lost-label .ic { width: 12px; height: 12px; flex: none; margin-top: 1px; color: var(--danger); }
.thread.resolved .quote { border-left-color: var(--ok); }

.msg { margin-bottom: 10px; }
.msg:last-child { margin-bottom: 0; }
.msg.reply {
  margin-left: 12px;
  padding-left: 12px;
  border-left: 1.5px solid var(--border-soft);
}
.msg-head { display: flex; align-items: center; gap: 7px; }
.msg .author { font-size: 12.5px; font-weight: 650; color: var(--text); }
.msg .time { font-size: 11px; color: var(--text-faint); }
.team-chip {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-quiet);
  background: var(--surface-sunken);
  border-radius: var(--r-xs);
  padding: 1px 5px;
}
.msg .body {
  font-size: 13.5px;
  line-height: 1.58;
  color: var(--text-body);
  white-space: pre-wrap;
  margin-top: 2px;
  word-wrap: break-word;
}
.mention { color: var(--accent); font-weight: 600; }
.msg .body a.doc-link {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px dashed var(--accent);
}
.msg .body a.body-link { color: var(--accent); word-break: break-all; }

/* "→ Say annual · in progress" — history sitting on the message that spawned
   it, so it reads as a small fact about that message, not a status pill. */
.msg-tickets { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.msg-ticket-chip {
  display: inline-flex;
  align-items: center;
  border: 0;
  padding: 2px 8px;
  border-radius: var(--r-pill);
  background: var(--surface-sunken);
  color: var(--text-quiet);
  font: inherit;
  font-size: 10.5px;
  font-weight: 600;
  cursor: pointer;
}
.msg-ticket-chip:hover { background: var(--surface-hover-card); color: var(--text); }

.thread-foot {
  display: flex;
  align-items: center;
  /* Wraps, because this row does not always fit. Reply, Agree, Disagree, a
     permalink and a menu need about 300px, and the comments pane is narrower
     than that on a small phone *and* on a desktop window around 870px wide.
     With nowrap the two icon buttons simply left the box — off the screen
     entirely at 320px, where nothing could reach them. Wrapping costs a line
     of height only when the alternative is losing the controls. */
  flex-wrap: wrap;
  gap: 6px;
  row-gap: 2px;
  padding: 9px 13px;
  border-top: 1px solid var(--border-soft);
  background: var(--surface-card-foot);
}
.text-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 0;
  background: none;
  border-radius: var(--r-sm);
  padding: 4px 7px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 550;
  color: var(--text-quiet);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.text-btn .ic { width: 13px; height: 13px; }
.text-btn:hover { background: var(--surface-sunken); color: var(--text); }
/* Reply is the action this pane exists for; Agree, Disagree, the permalink and
   the menu are the ones reached for occasionally. Five controls at identical
   weight made the row read as a toolbar rather than as an invitation to say
   something back. Colour is the whole change — nothing moves, so the careful
   wrapping worked out above still wraps the same way. */
.thread-foot .text-btn[data-action="focus-reply"] { color: var(--accent); font-weight: 650; }
.thread-foot .text-btn[data-action="focus-reply"]:hover { background: var(--accent-tint); color: var(--accent); }
.text-btn.danger:hover { color: var(--danger); background: var(--danger-bg); }
/* Zero basis so the spacer only ever fills what is genuinely left over. Given
   a real basis it would claim the first line on its own and push both icons
   onto a second one even when they would have fitted. */
.thread-foot .spacer { flex: 1; flex-basis: 0; min-width: 0; }
/* The permalink and the menu are one group, so they wrap together rather than
   the menu peeling off onto a line by itself. The auto margin keeps them on
   the right whichever line they end up on — the spacer does that job on the
   first line, and nothing would on a wrapped one. */
.thread-foot .foot-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-inline-start: auto;
}

/* ---------- decisions ---------- */

.decision-bar {
  display: flex;
  gap: 6px;
  padding: 0 13px 10px;
}
.text-btn.decide.on.accepted { color: var(--ok); background: var(--ok-bg); }
.text-btn.decide.on.rejected { color: var(--danger); background: var(--danger-bg); }
.decision-note {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 10px 0 0;
  padding: 8px 10px;
  border-radius: var(--r-md);
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-quiet);
  background: var(--surface-sunken);
}
.decision-note b { font-weight: 650; }
.decision-note .ic { width: 13px; height: 13px; flex: none; margin-top: 1px; }
.decision-note.accepted { color: var(--diff-add-text); background: var(--diff-add-bg); }
.decision-note.rejected { color: var(--diff-del-text); background: var(--diff-del-bg); }

/* ---- who decides, and the clock that decides if nobody does ---- */

/* Deliberately the same shape as .decision-note: they answer neighbouring
   questions and sit next to each other on the card, so a different treatment
   would read as a different kind of thing. */
.assignee-note,
.aging-note {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 10px 0 0;
  padding: 8px 10px;
  border-radius: var(--r-md);
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-quiet);
  background: var(--surface-sunken);
}
.assignee-note b,
.aging-note b { font-weight: 650; }
.assignee-note .ic,
.aging-note .ic { width: 13px; height: 13px; flex: none; margin-top: 1px; }

/* Yours reads as an obligation; somebody else's reads as information. */
.assignee-note.mine {
  color: var(--attn-text);
  background: var(--attn-bg);
  border: 1px solid var(--attn-border);
  padding: 7px 9px;
}

.aging-note { align-items: center; flex-wrap: wrap; }
.aging-note .aging-act {
  margin-left: auto;
  font-size: 11px;
  padding: 2px 8px;
  border-radius: var(--r-sm);
  white-space: nowrap;
}
.aging-note .aging-act:hover { background: var(--surface-hover-card); }

/* ---- a question that became work ---- */

/* Same shape as .assignee-note / .aging-note / .decision-note: answers a
   neighbouring question — is anybody doing this? — so it sits right beside
   them rather than inventing a new visual language for one more fact. */
.ticket-note {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin: 10px 0 0;
  padding: 8px 10px;
  border-radius: var(--r-md);
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-quiet);
  background: var(--surface-sunken);
}
.ticket-note b { font-weight: 650; }
.ticket-note .ic { width: 13px; height: 13px; flex: none; }
/* The ticket is finished — the same "all clear" tone the Needs-you screen's
   own empty state (.needs-tail) already uses. */
.ticket-note.done {
  color: var(--text-mid);
  background: var(--ok-bg);
  border: 1px solid var(--ok-border);
}
.ticket-note.done .ic { color: var(--ok); }
.ticket-note-act { margin-left: auto; white-space: nowrap; }

/* ---- documents waiting for you, on the For-you screen ---- */

.needs-approvals { margin: 0 0 22px; }
.needs-approvals h3 {
  margin: 0 0 10px;
  font-size: 13px;
  font-weight: 650;
  letter-spacing: -0.01em;
  color: var(--text-body);
}
.needs-approval {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  margin: 0 0 8px;
  padding: 11px 14px;
  border: 1px solid var(--attn-border);
  border-radius: var(--r-lg);
  background: var(--attn-bg);
  color: var(--attn-text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.needs-approval:hover { filter: brightness(0.985); }
.needs-approval:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.needs-approval .ic { width: 15px; height: 15px; flex: none; }
.na-who { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.na-who b { font-weight: 650; font-size: 13.5px; }
.needs-approval .member-email { color: var(--attn-text-quiet); }
.na-go { flex: none; font-size: 12px; font-weight: 650; }

/* ---- has a human confirmed this document? ---- */

/* Sits under the title, in the document column rather than the discussion
   pane, because it is a statement about the thing being read rather than a
   conversation about it. */
.doc-signoff {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 0 0 18px;
  padding: 9px 13px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--text-quiet);
  background: var(--surface-sunken);
}
.doc-signoff .ic { width: 14px; height: 14px; flex: none; }
.doc-signoff .so-text { flex: 1; min-width: 12rem; }
.doc-signoff .so-act { flex: none; font-size: 12px; padding: 3px 10px; }

.doc-signoff.ok {
  color: var(--diff-add-text);
  background: var(--diff-add-bg);
  border-color: var(--ok-border);
}

/* Approved and then edited. Warned about rather than quietly downgraded to
   "unapproved": the approval is what makes the edit worth noticing. */
.doc-signoff.stale {
  color: var(--attn-text);
  background: var(--attn-bg);
  border-color: var(--attn-border);
}
.doc-signoff.pending { color: var(--text-quiet); }
.doc-signoff.none { color: var(--text-faint); background: none; border-style: dashed; }

.member-tag.member-ok { color: var(--ok); background: var(--ok-bg); }
.member-tag.member-stale { color: var(--attn-text); background: var(--attn-bg); }

/* ---- asking somebody to decide ---- */

.assign-list { max-height: 46vh; overflow-y: auto; margin: 10px 0 4px; }
.assign-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 7px 8px;
  border: 0;
  border-radius: var(--r-md);
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.assign-row:hover { background: var(--surface-hover-card); }
.assign-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.assign-who { display: flex; flex-direction: column; min-width: 0; }
.assign-who b { font-weight: 600; font-size: 13px; }
#assign-quote {
  margin: 4px 0 12px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-quiet);
}

/* ---- teams ---- */

/* Wider than the other dialogs: three lists that are read together, and a
   narrow column would make them three screens with scrolling between. */
#team-body { max-height: 68vh; overflow-y: auto; padding-right: 2px; }
.team-sec { margin: 0 0 22px; }
.team-sec:last-child { margin-bottom: 8px; }
.team-sec h4 {
  margin: 0 0 8px;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.team-switch { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 18px; }
.team-switch .chip.on { background: var(--accent-tint); border-color: var(--accent); color: var(--accent); }
.team-role {
  font: inherit;
  font-size: 12px;
  padding: 3px 6px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--card);
  color: var(--text-body);
}
.team-role:disabled { opacity: 0.6; border-color: transparent; background: none; }
.team-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  color: var(--text-quiet);
  cursor: pointer;
}
.team-toggle input { accent-color: var(--accent); }

/* A setting whose consequence is not guessable from its label gets a sentence.
   These two decide who can read a team's documents and who can be let near
   them — worth a line each rather than a switch somebody flips to find out. */
.team-setting {
  /* Not inline-flex, which .team-toggle is: these are full-width rows in a
     dialog, and as an inline box the text column collapsed to min-content and
     wrapped one character per line. */
  display: flex;
  width: 100%;
  align-items: flex-start;
  gap: 9px;
  padding: 9px 0;
  border-top: 1px solid var(--border-soft);
}
.team-setting:first-of-type { border-top: 0; padding-top: 0; }
.team-setting input { margin-top: 2px; flex: none; }
/* flex: 1 and min-width: 0 together are what let the sentence use the row and
   wrap on words rather than on characters. */
.team-setting > span { display: flex; flex: 1; min-width: 0; flex-direction: column; gap: 2px; }
.team-setting b { font-weight: 650; color: var(--text-body); }
.setting-why { font-size: 12px; line-height: 1.45; color: var(--text-faint); }
#team-repo {
  font: inherit;
  flex: 1;
  padding: 7px 9px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--card);
  color: var(--text-body);
}
.thread-foot .icon-btn { width: 27px; height: 27px; }
.thread-foot .icon-btn:hover { background: var(--surface-sunken); }

.foot-menu { position: relative; }
.foot-menu-pop {
  position: absolute;
  right: 0;
  bottom: 32px;
  z-index: 20;
  min-width: 150px;
  background: var(--card);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2), 0 0 0 1px var(--border);
  padding: 4px;
  animation: pop-in 0.12s ease;
}
/* A reply's own overflow — the same idea as .foot-menu above it, opening
   downward instead of upward because a reply can sit anywhere in a long
   thread, not only at the card's foot. */
.msg-menu { position: relative; margin-left: auto; flex: none; }
.msg-menu .icon-btn { width: 22px; height: 22px; }
.msg-menu-pop {
  position: absolute;
  right: 0;
  top: 26px;
  z-index: 20;
  min-width: 150px;
  background: var(--card);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2), 0 0 0 1px var(--border);
  padding: 4px;
  animation: pop-in 0.12s ease;
}
.foot-menu-pop button, .msg-menu-pop button {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  border: 0;
  background: none;
  padding: 7px 10px;
  font-family: inherit;
  font-size: 12.5px;
  color: var(--text-mid);
  text-align: left;
  border-radius: var(--r-sm);
  cursor: pointer;
}
.foot-menu-pop button:hover, .msg-menu-pop button:hover { background: var(--surface-hover-card); }
.foot-menu-pop button.danger:hover { color: var(--danger); background: var(--danger-bg); }

/* live reply row on the active / needs-you card */
.reply-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 13px;
  border-top: 1px solid var(--border-soft);
  background: var(--surface-card-foot);
}
.reply-input {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  padding: 6px 10px;
  font-family: inherit;
  font-size: 12.5px;
  background: var(--card);
  color: var(--text);
}
.reply-input:focus { outline: none; border-color: var(--accent); }
/* A textarea now, so a modifier+Enter can put a real line break in a reply.
   It grows with its content and stops at ~6 lines, then scrolls. */
textarea.reply-input {
  resize: none;
  overflow-y: auto;
  max-height: 7.5em;
  line-height: 1.45;
}
.reply-row .icon-btn {
  width: 30px;
  height: 30px;
  background: var(--surface-sunken);
  color: var(--text-quiet);
}
.reply-row .icon-btn:hover { background: var(--surface-hover); color: var(--text); }
/* Three buttons now share this row with the input: keep them from being
   squeezed, and let the input take whatever is left. */
.reply-row { gap: 6px; }
.reply-row .icon-btn,
.reply-row .rec-time { flex: none; }
.reply-row .icon-btn .ic { width: 15px; height: 15px; }

.resolved-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
  width: 100%;
  padding: 10px 12px;
  border: 0;
  border-radius: var(--r-lg);
  background: var(--resolved-summary);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--text-quiet);
  cursor: pointer;
  text-align: left;
}
.resolved-summary .ic { width: 14px; height: 14px; color: var(--ok); }
.resolved-summary .rs-action { margin-left: auto; color: var(--text-faint); }

.empty {
  color: var(--text-soft);
  font-size: 13px;
  padding: 30px 18px;
  line-height: 1.6;
  text-align: center;
}
.empty .empty-icon { display: inline-flex; color: var(--text-faint); margin-bottom: 8px; }
.empty .empty-icon .ic { width: 26px; height: 26px; }
.empty strong { display: block; color: var(--text); font-size: 13.5px; font-weight: 650; margin-bottom: 3px; }

/* ============================================================================
   Floating UI
   ========================================================================= */

.floaty { position: fixed; z-index: 50; }

/* Animates `scale` and `translate` rather than `transform`, and the difference
   is not cosmetic.

   Every dialog is centred with `transform: translateX(-50%)`, and this
   animation used to set `transform` itself — `translateY(4px) scale(0.96)`,
   then `none`. Both of those *replace* the centring for as long as the
   animation runs, so a dialog opened half a width right of centre and snapped
   left when it finished. Wide enough viewport and it read as a flicker; on a
   phone the dialog spent that time hanging off the right edge.

   `scale` and `translate` are separate properties that compose with
   `transform` instead of overwriting it, so the centring survives. */
@keyframes pop-in {
  from { opacity: 0; scale: 0.96; translate: 0 4px; }
  to { opacity: 1; scale: 1; translate: 0 0; }
}

/* ---------- selection action bar ---------- */

#sel-bar {
  display: flex;
  align-items: stretch;
  background: var(--ink);
  border-radius: var(--r-lg);
  padding: 4px;
  box-shadow: var(--e3);
  animation: pop-in 0.14s ease;
}
.sel-item, .sel-icon {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 0;
  background: none;
  border-radius: var(--r-sm);
  padding: 7px 11px;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-text);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.12s ease;
}
.sel-item .si-icon { display: inline-flex; color: var(--ink-accent); }
.sel-item .si-icon .ic { width: 14px; height: 14px; }
.sel-item.soft { font-weight: 550; color: var(--ink-text-soft); }
.sel-item.soft .si-icon { color: var(--ink-text-soft); }
.sel-item + .sel-item, #sel-bar .sel-icon { border-left: 1px solid var(--ink-line); border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.sel-item + .sel-item { border-radius: 0; }
.sel-icon { width: 30px; padding: 0; justify-content: center; color: var(--ink-text-soft); }
.sel-item:hover, .sel-icon:hover { background: var(--ink-raised); }

/* ---------- composer ---------- */

#composer {
  width: 372px;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-2);
  padding: 13px 14px 12px;
  animation: pop-in 0.14s ease;
}
#composer .quote { margin-bottom: 11px; max-height: 62px; }
.composer-input { display: flex; gap: 9px; align-items: flex-start; }
.ci-fields { flex: 1; min-width: 0; }
#composer input, #composer textarea {
  width: 100%;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  padding: 5px 0;
  font-family: inherit;
  font-size: 13.5px;
  background: transparent;
  color: var(--text);
  resize: none;
}
#composer input { border-bottom-color: var(--border); margin-bottom: 4px; padding: 5px 2px; }
#composer textarea { min-height: 46px; line-height: 1.5; }
#composer input:focus, #composer textarea:focus { outline: none; }
#composer textarea::placeholder { color: var(--text-faint); }
.composer-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border-soft);
}
.cf-note { flex: 1; min-width: 0; font-size: 11px; color: var(--text-faint); }
.composer-foot .ghost-btn { border: 0; padding: 6px 8px; }
.composer-foot .ghost-btn:hover { background: var(--surface-hover-card); color: var(--text); }

/* ---------- attachments ---------- */

#att-mic-btn.recording,
.reply-row .icon-btn.recording {
  color: var(--danger);
  animation: rec-pulse 1.2s ease infinite;
}
@keyframes rec-pulse {
  50% { opacity: 0.45; }
}
.rec-time {
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  color: var(--danger);
}
#composer-atts,
.reply-atts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 2px;
}
/* Indented to line up with the reply box rather than the avatar beside it. */
.reply-atts { margin: 6px 0 0 30px; }
.p-att {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 8px);
  background: var(--surface-sunken);
  padding: 4px 6px;
  font-size: 11.5px;
  color: var(--text-soft);
}
.p-att img {
  width: 46px;
  height: 46px;
  object-fit: cover;
  border-radius: 5px;
  display: block;
}
.p-att .ic { width: 13px; height: 13px; color: var(--text-faint); }
.p-att.uploading { opacity: 0.5; }
.p-att-name { max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.p-att-tr { color: var(--ok); font-weight: 650; font-size: 10.5px; }
.p-att-x {
  display: inline-flex;
  border: 0;
  background: none;
  color: var(--text-faint);
  cursor: pointer;
  padding: 2px;
}
.p-att-x .ic { width: 11px; height: 11px; }
.p-att-x:hover { color: var(--danger); }

.msg-atts {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 6px 0 0 34px;
}
.msg.reply .msg-atts { margin-left: 32px; }
.att-img { display: inline-block; max-width: 240px; border-radius: var(--r-md); overflow: hidden; border: 1px solid var(--border); }
.att-img img { display: block; max-width: 100%; max-height: 180px; object-fit: cover; }
.att-img:hover { border-color: var(--border-hover); }
.att-audio { display: flex; flex-direction: column; gap: 4px; }
.att-audio audio { width: 100%; max-width: 280px; height: 34px; }
.att-tr {
  font-size: 12px;
  font-style: italic;
  color: var(--text-quiet);
  line-height: 1.5;
}

/* ---------- dialogs ---------- */

/* Centred with auto margins rather than `translateX(-50%)`, so that centring
   does not live in `transform` — which the pop-in animation needs, and which
   anything else that touches transform would silently break. With both
   insets 0 and a definite width, auto margins split the leftover space. */
#pr-form, #repo-form, #new-doc-form, #link-form, #agent-form, #diagram-form, #chat-move-form,
#members-form, #account-form, #usage-form, #confirm-form, #assign-form, #team-form,
#approvers-form, #ticket-form {
  width: 420px;
  left: 0;
  right: 0;
  margin-inline: auto;
  top: 22%;
  background: var(--card);
  border-radius: var(--r-2xl);
  box-shadow: var(--e4), 0 0 0 1px var(--border);
  padding: 22px;
  animation: pop-in 0.16s ease;
}
#diagram-form {
  width: min(680px, calc(100vw - 32px));
  top: 7%;
  max-height: 88vh;
  overflow-y: auto;
}

/* Wider and taller than the standard dialog: the three lists a team is made of
   are read together, and a 420px column would turn them into three screens
   with scrolling in between. Must follow the shared rule above, not precede
   it — same specificity, so the later one wins. */
#team-form {
  width: min(620px, calc(100vw - 32px));
  top: 8%;
  max-height: 84vh;
  overflow-y: auto;
}
#diagram-form h3 { margin: 0 0 3px; font-size: 16px; }
#diagram-form input:not([type='file']) {
  font-family: inherit;
  font-size: 13.5px;
  padding: 7px 9px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm, 7px);
  background: var(--card);
  color: var(--text);
}
#diagram-form input:focus, #dg-src:focus, .dg-row select:focus { outline: none; border-color: var(--accent); }
#diagram-form .row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
#new-doc-form {
  width: 460px;
  top: 12%;
  max-height: 82vh;
  overflow-y: auto;
}
#pr-form h3, #repo-form h3, #new-doc-form h3, #link-form h3, #agent-form h3, #members-form h3,
#account-form h3, #usage-form h3, #confirm-form h3, #assign-form h3, #team-form h3,
#approvers-form h3, #ticket-form h3 {
  /* A dialog title is a title, and every other title in this product — the
     article's H1, the For-you headline, Tasks, Analytics — is set in the
     document voice. These were the last sans ones left. */
  margin: 0 0 3px;
  font-family: var(--font-doc);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.005em;
}

/* Asking before something irreversible. The confirming button carries the
   weight when the answer cannot be undone, so the question is legible from the
   buttons alone rather than only from the sentence above them. */
#confirm-form .row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
#confirm-form .form-sub { margin-bottom: 0; }
button.primary.danger { background: var(--danger); border-color: var(--danger); }
button.primary.danger:hover { filter: brightness(0.94); }
.form-sub { margin: 0 0 16px; font-size: 12.5px; color: var(--text-soft); }
.field-label {
  display: block;
  margin-bottom: 5px;
  font-size: 12px;
  font-weight: 650;
  color: var(--text-mid);
}
/* Text fields, and deliberately not checkboxes.
   This sets width:100%, padding, a border and a bottom margin — every one of
   which is wrong for a checkbox, which wants to be 13px and stay there. The
   exclusion is here rather than fought with higher specificity per dialog,
   because `#form input` beats any class selector a checkbox rule could use
   and the next person to add a checkbox would hit exactly this again. */
#pr-form input:not([type='checkbox']), #pr-form textarea,
#repo-form input:not([type='checkbox']), #repo-form textarea,
#new-doc-form input:not([type='checkbox']), #new-doc-form select,
#link-form input:not([type='checkbox']), #agent-form input:not([type='checkbox']), #agent-form select,
#chat-move-form input:not([type='checkbox']), #members-form input:not([type='checkbox']),
#account-form input:not([type='checkbox']),
#assign-form input:not([type='checkbox']), #team-form input:not([type='checkbox']),
#approvers-form input:not([type='checkbox']),
#ticket-form input:not([type='checkbox']), #ticket-form textarea {
  width: 100%;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  padding: 8px 11px;
  font-family: inherit;
  font-size: 13.5px;
  margin-bottom: 13px;
  resize: vertical;
  background: var(--bg);
  color: var(--text);
}
#pr-form input:focus, #pr-form textarea:focus,
#repo-form input:focus, #repo-form textarea:focus,
#new-doc-form input:focus, #new-doc-form select:focus, #link-form input:focus, #agent-form input:focus, #agent-form select:focus,
#chat-move-form input:not([type='checkbox']):focus,
#members-form input:not([type='checkbox']):focus,
#account-form input:not([type='checkbox']):focus,
#assign-form input:not([type='checkbox']):focus,
#team-form input:not([type='checkbox']):focus,
#approvers-form input:not([type='checkbox']):focus,
#ticket-form input:not([type='checkbox']):focus, #ticket-form textarea:focus {
  outline: none;
  border-color: var(--accent);
  border-width: 1.5px;
  box-shadow: 0 0 0 3px rgba(79, 70, 214, 0.1);
}
#pr-form .row, #repo-form .row, #new-doc-form .row, #link-form .row, #agent-form .row,
#members-form .row, #account-form .row, #usage-form .row, #assign-form .row, #team-form .row,
#approvers-form .row, #ticket-form .row {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 4px;
}

/* ---------- make it a ticket ---------- */

/* Same shape as .assignee-note / .aging-note: a fact about the message being
   promoted, not a warning — duplicate protection is something a person sees,
   not a rule that argues with them. */
.ticket-existing {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 0 0 14px;
  padding: 8px 10px;
  border-radius: var(--r-md);
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-quiet);
  background: var(--surface-sunken);
}
.ticket-existing .ic { width: 13px; height: 13px; flex: none; margin-top: 1px; }
.ticket-link {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}
.ticket-link:hover { filter: brightness(0.9); }

/* The assignee is picked, never typed — same reasoning as the @mention
   dropdown this borrows its look from: a name gone wrong here silently
   assigns work to the wrong person. */
.ticket-assignee-wrap { position: relative; }
.ticket-assignee-menu {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  z-index: 5;
  margin: 2px 0 0;
  padding: 4px;
  list-style: none;
  background: var(--card);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2), 0 0 0 1px var(--border);
  max-height: 200px;
  overflow-y: auto;
}

/* ---------- new document ---------- */

#nd-templates {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
  border: 0;
  padding: 0;
  margin: 0 0 16px;
}
#nd-templates legend { margin-bottom: 7px; padding: 0; }
.nd-tpl {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
  padding: 9px 11px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--bg);
  font-family: inherit;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.nd-tpl:hover { border-color: var(--border-hover); background: var(--surface-hover-card); }
.nd-tpl.on { border-color: var(--accent); background: var(--accent-wash); box-shadow: 0 0 0 1px var(--accent); }
.nd-tpl-name { font-size: 13px; font-weight: 650; color: var(--text); }
.nd-tpl.on .nd-tpl-name { color: var(--accent); }
.nd-tpl-blurb { font-size: 11.5px; line-height: 1.35; color: var(--text-soft); }

#nd-new-folder { margin-top: -4px; }

.nd-preview {
  margin: 2px 0 6px;
  font-size: 12.5px;
  color: var(--text-quiet);
}
.nd-preview code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  color: var(--text);
  background: var(--surface-sunken);
  padding: 1px 5px;
  border-radius: 5px;
}
.nd-preview.bad, .nd-preview.bad code { color: var(--danger); }
.nd-landing {
  margin: 0 0 14px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-soft);
}
#nd-submit:disabled { opacity: 0.45; cursor: not-allowed; }

/* ---------- connect a coding agent ---------- */

/* `top` is lifted off the shared 22% deliberately: 22 + 84 is 106vh, so on a
   800px-tall laptop the foot of a full-height dialog — here the token list and
   its Revoke buttons — sat below the fold with no way to scroll to it. */
#agent-form { width: 500px; top: 8%; max-height: 84vh; overflow-y: auto; }
.agent-config {
  margin: 0 0 10px;
  padding: 11px 13px;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--text-body);
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 210px;
  overflow-y: auto;
}
.agent-token {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-top: 1px solid var(--border-soft);
  font-size: 13px;
}
.agent-token span { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.agent-token em { font-style: normal; font-size: 11.5px; color: var(--text-soft); }
.agent-token .ghost-btn { padding: 4px 10px; font-size: 12px; }
#agent-form .derived-pill { font-family: var(--font-mono); text-transform: none; letter-spacing: 0; }
.agent-scope-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
}

/* Machine-authored Markdown, at comment scale — not the article's
   .markdown-body, whose heading and paragraph rhythm would make one reply
   tower over the conversation. */
/* .msg .body keeps pre-wrap for humans' PLAIN-TEXT comments — their typed
   line breaks are the formatting. Rendered HTML is the opposite case: the
   markdown renderer emits newlines BETWEEN tags, and pre-wrap turned each
   one into a visible blank line. Same-or-higher specificity than
   .msg .body, or both declarations lose. */
.msg .body.msg-md, .msg-md { white-space: normal; line-height: 1.45; }
.msg-md p { margin: 0 0 4px; }
.msg-md p:last-child { margin-bottom: 0; }
.msg-md ul, .msg-md ol { margin: 0 0 4px; padding-left: 20px; }
.msg-md li { margin: 0 0 2px; }
/* Models blank-line their bullets, which Markdown reads as a "loose list"
   and wraps every item in a paragraph — paragraph spacing then stacks on
   list spacing and a five-line answer fills half the rail. Inside a list
   item, paragraphs keep only a whisper of separation. */
.msg-md li p { margin: 0 0 2px; }
.msg-md li p:last-child { margin-bottom: 0; }
.msg-md code {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.92em;
  background: var(--surface-sunken);
  border-radius: 3px;
  padding: 0 4px;
}
.msg-md pre {
  margin: 0 0 8px;
  padding: 8px 10px;
  background: var(--surface-sunken);
  border-radius: 6px;
  overflow-x: auto;
}
.msg-md pre code { background: none; padding: 0; }
.msg-md blockquote {
  margin: 0 0 8px;
  padding-left: 10px;
  border-left: 2px solid var(--border);
  color: var(--text-soft);
}
.msg-md hr { border: 0; border-top: 1px solid var(--border); margin: 10px 0; }

/* A withheld comment in the analytics follow-up queue is a statement about
   scope, not content — mute it so it cannot be misread as what was said. */
.a-withheld { color: var(--text-soft); font-style: italic; }

/* A reply in flight: visibly present, visibly not yet real. */
.msg-pending { opacity: 0.55; }
.msg-pending .time { font-style: italic; animation: msg-pending-pulse 1.2s ease-in-out infinite; }
@keyframes msg-pending-pulse { 50% { opacity: 0.4; } }
@media (prefers-reduced-motion: reduce) { .msg-pending .time { animation: none; } }

/* A machine spoke — say so wherever the message renders. */
.agent-badge {
  background: var(--surface-sunken);
  color: var(--text-soft);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  padding: 0 5px;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ---------- @mention autocomplete ---------- */

/**
 * Above the composer (50) and the mobile comment sheet (60), because it is
 * always opened *from* a field inside one of them; below the search modal
 * (80), which it never coexists with. Sharing .floaty's z-index put it behind
 * whichever layer came later in the DOM.
 */
#mention-menu {
  z-index: 70;
  min-width: 250px;
  max-width: 320px;
  background: var(--card);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-2), 0 0 0 1px var(--border);
  padding: 4px;
  max-height: 230px;
  overflow-y: auto;
  animation: pop-in 0.12s ease;
}
.mention-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  border: 0;
  background: none;
  padding: 7px 10px;
  font-family: inherit;
  text-align: left;
  border-radius: var(--r-md);
  cursor: pointer;
}
.mention-item.sel, .mention-item:hover { background: var(--accent-wash); }
.mention-item .m-text { min-width: 0; }
.mention-item .m-name {
  display: block;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mention-item .m-detail {
  display: block;
  font-size: 11.5px;
  color: var(--text-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- search (⌘K) ---------- */

#search-modal { position: fixed; inset: 0; z-index: 80; }
#search-scrim {
  position: absolute;
  inset: 0;
  background: rgba(25, 24, 23, 0.34);
  backdrop-filter: blur(1.5px);
}
#search-panel {
  position: relative;
  width: min(620px, calc(100vw - 32px));
  margin: 78px auto 0;
  max-height: min(560px, calc(100vh - 120px));
  display: flex;
  flex-direction: column;
  background: var(--card);
  border-radius: var(--r-2xl);
  box-shadow: var(--e4);
  overflow: hidden;
  animation: pop-in 0.14s ease;
}
.sp-query {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 15px 18px;
  border-bottom: 1px solid var(--border-soft);
  flex: none;
}
.sp-query .sp-icon { display: inline-flex; color: var(--text-soft); }
.sp-query .sp-icon .ic { width: 17px; height: 17px; }
#search-input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 16px;
  color: var(--text);
}
#search-input:focus { outline: none; }
#search-input::placeholder { color: var(--text-faint); }
.sp-query kbd {
  background: var(--surface-sunken);
  border-radius: var(--r-xs);
  padding: 2px 6px;
  flex: none;
}
#search-results { flex: 1; min-height: 0; overflow-y: auto; padding: 6px; }
#search-results .empty { padding: 34px 18px; }
.sr-group {
  padding: 8px 10px 5px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.sr-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  border: 0;
  background: none;
  padding: 9px 10px;
  border-radius: var(--r-md);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.sr-row:hover { background: var(--surface-hover-card); }
.sr-row.sel { background: var(--accent-wash); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 25%, transparent); }
.sr-icon { display: inline-flex; color: var(--text-faint); padding-top: 1px; }
.sr-text { min-width: 0; flex: 1; }
.sr-body {
  display: block;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--text-body);
  overflow: hidden;
}
.sr-body .hit { background: var(--hl); border-radius: 2px; padding: 0 2px; font-style: normal; }
.sr-title {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sr-meta, .sr-dim {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--text-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The author and path share this line with a "Needs you" badge, and one of the
   two has to give when the line is too narrow. It should be the path: it is
   long, it is already abbreviated elsewhere, and a truncated path still reads
   as a path. The badge does not — on a 320px screen it was being ellipsised to
   "Ne…", which is not a word. So the path shrinks and the badge does not. */
.sr-meta {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}
.sr-where {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sr-needs {
  flex: none;
  background: var(--attn-bg);
  color: var(--attn-text);
  font-weight: 650;
  border-radius: var(--r-xs);
  padding: 1px 5px;
}
.sp-foot {
  display: flex;
  gap: 16px;
  flex: none;
  padding: 9px 16px;
  border-top: 1px solid var(--border-soft);
  background: var(--surface-card-foot);
  font-size: 11px;
  color: var(--text-soft);
}

/* ============================================================================
   Editor
   ========================================================================= */

#edit-banner {
  max-width: 760px;
  margin: 16px auto 0;
  padding: 11px 15px;
  background: var(--success-bg);
  border: 1px solid var(--success-border);
  border-radius: var(--r-lg);
  font-size: 13.5px;
  color: var(--text-body);
  animation: fade-up 0.2s ease;
}
#edit-banner code { font-family: var(--font-mono); font-size: 0.9em; }
#edit-banner a { color: var(--accent); font-weight: 600; }
/* Not every banner is good news. A rule about what a document cannot do is a
   statement of fact, and the green of a saved draft would misread it. */
#edit-banner.note {
  background: var(--card);
  border-color: var(--border);
  color: var(--text-soft);
}
#edit-banner.note strong { color: var(--text); }
/* What the renderer refused. Set smaller and quieter than the rule above it:
   the sentence is the news, and this is the detail somebody chasing a missing
   chart needs — the hostnames in it are the document author's own words. */
#edit-banner .banner-warnings {
  margin-top: 8px;
  font-size: 12.5px;
  color: var(--text-quiet);
}
#edit-banner .banner-warnings ul {
  margin: 5px 0 0;
  padding-left: 18px;
}
#edit-banner .banner-warnings li {
  margin-top: 2px;
  /* A refusal quotes a url the document asked for, and a long one would
     otherwise push the banner past the width of the pane. */
  overflow-wrap: anywhere;
}

/* ---------- "a new version is available" ---------- */

/*
 * Bottom centre, above the toasts, and it stays until it is answered. It sits
 * over the content rather than reflowing it: arriving unannounced and pushing
 * somebody's paragraph up the screen would be its own small betrayal.
 */
#update-bar {
  position: fixed;
  left: 50%;
  /* Clear of #toasts, which owns bottom: 22px on the same centre line. A
     transient toast must not cover a notice that is waiting to be answered. */
  bottom: 76px;
  transform: translate(-50%, 12px);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: calc(100vw - 32px);
  padding: 11px 12px 11px 18px;
  border-radius: var(--r-pill, 999px);
  /* --ink flips meaning between the themes — dark in light, amber in dark —
     and its partners flip with it. Pairing them is what keeps this legible in
     both; --ink with a hand-picked white was invisible in dark mode. */
  background: var(--ink);
  color: var(--ink-text);
  font-size: 13.5px;
  box-shadow: var(--e3);
  opacity: 0;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
#update-bar.show { opacity: 1; transform: translate(-50%, 0); }
#update-bar button {
  border: 0;
  cursor: pointer;
  font: inherit;
  border-radius: var(--r-pill, 999px);
  padding: 6px 14px;
}
#update-bar .ub-reload {
  background: var(--ink-accent);
  color: var(--ink);
  font-weight: 600;
}
#update-bar .ub-later {
  background: none;
  color: var(--ink-text-soft);
  padding-inline: 6px;
}
#update-bar .ub-later:hover { color: var(--ink-text); }

@media (max-width: 768px) {
  /* Clear of the browser's own bottom chrome on a phone. */
  #update-bar { bottom: 68px; left: 12px; right: 12px; transform: translateY(12px); max-width: none; }
  #update-bar.show { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  #update-bar { transition: none; }
}

/* Editing is a mode, not a pane: the rail and both panes step aside. */
#app.editing { grid-template-columns: minmax(0, 1fr); }
#app.editing #files-pane,
#app.editing #doc-pane,
#app.editing #needs-pane,
#app.editing #comments-pane { display: none; }

/* The editor takes over the whole app grid: toolbar, then work area + panel. */
#editor-wrap {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100vh;
  background: var(--bg);
  animation: fade-up 0.2s ease;
}
#editor-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
  padding: 13px 24px;
  background: var(--bg-soft);
  border-bottom: 1px solid var(--border-soft);
  flex-wrap: wrap;
}
#editor-bar .spacer { flex: 1; }
.ed-lead {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  color: var(--text-mid);
}
.ed-lead .ed-pencil { display: inline-flex; color: var(--accent); }
.ed-lead .ed-pencil .ic { width: 14px; height: 14px; }
.ed-path {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-mid);
  background: var(--surface-hover);
  border-radius: var(--r-xs);
  padding: 3px 7px;
}
.ed-promise {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--attn-text);
  background: var(--attn-bg);
  border-radius: var(--r-pill);
  padding: 4px 10px;
}

.seg {
  display: flex;
  gap: 2px;
  padding: 2px;
  background: var(--surface-hover);
  border-radius: var(--r-md);
}
.seg-btn {
  border: 0;
  background: transparent;
  border-radius: var(--r-sm);
  padding: 5px 11px;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 550;
  color: var(--text-quiet);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.seg-btn b { font-weight: 650; color: var(--accent); }
.seg-btn b:empty { display: none; }
.seg-btn.active {
  background: var(--card);
  color: var(--text);
  font-weight: 600;
  box-shadow: var(--shadow-1);
}

#editor-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 400px;
  flex: 1;
  min-height: 0;
}
#editor-body.no-panel { grid-template-columns: minmax(0, 1fr); }
#editor-body.no-panel #editor-side { display: none; }
#editor-main { overflow-y: auto; padding: 28px 32px 60px; min-width: 0; }
#editor-card {
  max-width: 720px;
  margin: 0 auto;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  overflow: hidden;
}
.ed-strip {
  display: flex;
  align-items: center;
  padding: 9px 16px;
  border-bottom: 1px solid var(--border-soft);
  background: var(--surface-card-foot);
}
.ed-lang { font-family: var(--font-mono); font-size: 11.5px; color: var(--text-soft); }
.ed-changed { margin-left: auto; font-size: 11.5px; color: var(--text-soft); }
#editor {
  display: block;
  width: 100%;
  min-height: 58vh;
  border: 0;
  padding: 18px 20px;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.85;
  resize: vertical;
  background: var(--card);
  color: var(--text-body);
}
#editor:focus { outline: none; }
#editor-preview { padding: 8px 24px 24px; max-width: none; }

/* ---------- rich editor ---------- */

#rich-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  padding: 7px 12px;
  border-bottom: 1px solid var(--border-soft);
  background: var(--surface-card-foot);
  position: sticky;
  top: 0;
  z-index: 2;
}
.rt-btn {
  /* Holds a text glyph on some tools and an SVG on others, so it centres its
     content rather than relying on the line box. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 28px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--text-mid);
  font-family: inherit;
  font-size: 12.5px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.rt-btn:hover { background: var(--surface-hover-card); color: var(--text); border-color: var(--border); }
.rt-btn.on { background: var(--accent-wash); color: var(--accent); border-color: var(--accent-tint); }
.rt-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rt-btn b, .rt-btn i, .rt-btn s { font-size: 13px; }
.rt-btn .ic { width: 15px; height: 15px; }
.rt-sep {
  width: 1px;
  height: 18px;
  margin: 0 5px;
  background: var(--border);
}

#rich-editor {
  min-height: 54vh;
  padding: 18px 24px 28px;
  max-width: none;
  background: var(--card);
}
#rich-editor:focus { outline: none; }
#rich-editor > :first-child { margin-top: 0; }
/* An empty document should still show where the caret is. */
#rich-editor p:empty::after { content: ''; display: inline-block; }
#rich-editor:empty::before {
  content: 'Start writing…';
  color: var(--text-soft);
}
#rich-editor a { cursor: text; }
.ed-primer {
  max-width: 720px;
  margin: 14px auto 0;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--text-soft);
}
.ed-primer code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: var(--surface-sunken);
  border-radius: var(--r-xs);
  padding: 1px 4px;
}

/* ---------- diff ("Changes") ---------- */

#editor-diff {
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.85;
  padding: 6px 0;
  overflow-x: auto;
}
.diff-row { display: flex; white-space: pre-wrap; word-break: break-word; }
.diff-row .dl {
  flex: none;
  width: 46px;
  padding-right: 10px;
  text-align: right;
  color: var(--text-faint);
  user-select: none;
}
.diff-row .dm { flex: none; width: 16px; text-align: center; user-select: none; }
.diff-row .dt { flex: 1; min-width: 0; padding-right: 16px; }
.diff-row.add { background: var(--diff-add-bg); color: var(--diff-add-text); }
.diff-row.add .dm, .diff-row.add .dl { color: var(--diff-add-gutter); }
.diff-row.del { background: var(--diff-del-bg); color: var(--diff-del-text); }
.diff-row.del .dm, .diff-row.del .dl { color: var(--diff-del-gutter); }
.diff-gap {
  padding: 4px 0 4px 46px;
  color: var(--text-faint);
  background: var(--surface-sunken);
  border-top: 1px solid var(--border-soft);
  border-bottom: 1px solid var(--border-soft);
  font-size: 11.5px;
}
.diff-empty { padding: 28px 20px; text-align: center; color: var(--text-soft); font-family: var(--font-ui); font-size: 13px; }

/* ---------- send-for-review panel ---------- */

#editor-side {
  background: var(--bg-soft);
  border-left: 1px solid var(--border);
  padding: 24px 22px 40px;
  overflow-y: auto;
}
.es-title { margin: 0 0 4px; font-size: 14.5px; font-weight: 650; letter-spacing: -0.01em; }
.es-sub { margin: 0 0 16px; font-size: 12.5px; line-height: 1.5; color: var(--text-soft); }
#editor-side .field-label { margin-top: 14px; }
#editor-side input, #editor-side textarea {
  width: 100%;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  padding: 8px 11px;
  font-family: inherit;
  font-size: 13.5px;
  background: var(--card);
  color: var(--text);
  resize: vertical;
}
#editor-side input:focus, #editor-side textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(79, 70, 214, 0.1);
}
.es-card {
  margin-top: 20px;
  padding: 15px 16px;
  background: var(--card);
  border-radius: var(--r-lg);
  box-shadow: 0 0 0 1px var(--border);
}
.es-card h3 { margin: 0 0 10px; font-size: 12.5px; font-weight: 650; color: var(--text); }
.es-card ol { margin: 0; padding: 0; list-style: none; }
.es-card li {
  position: relative;
  padding: 0 0 12px 18px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-quiet);
}
.es-card li:last-child { padding-bottom: 0; }
.es-card li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--idle-dot);
}
.es-card li:first-child::before { background: var(--accent); }
.es-card li::after {
  content: "";
  position: absolute;
  left: 3px;
  top: 15px;
  bottom: 2px;
  width: 1px;
  background: var(--border);
}
.es-card li:last-child::after { display: none; }
.es-card code { font-family: var(--font-mono); font-size: 0.92em; color: var(--text-mid); }
.es-tell {
  margin: 16px 0 0;
  padding: 10px 12px;
  border-radius: var(--r-md);
  background: var(--attn-bg);
  font-size: 12px;
  line-height: 1.5;
  color: var(--attn-text);
}
.es-send { width: 100%; justify-content: center; margin-top: 20px; padding: 11px; font-size: 14px; }
#es-conflict {
  padding: 14px 15px;
  border-radius: var(--r-lg);
  background: var(--danger-bg);
  border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-body);
}
#es-conflict strong { display: block; color: var(--danger); margin-bottom: 5px; }
#es-conflict .row { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }

/* ============================================================================
   Mobile
   ========================================================================= */

#nav-btn { display: none; }

/* Closing the discussion pane gives the reader the full page; the margin
   chips keep the conversations visible. Desktop only — on phones the pane is
   already a drawer. */
@media (min-width: 1280px) {
  #app.pane-closed { grid-template-columns: var(--rail-w) minmax(0, 1fr); }
}
@media (max-width: 1279px) and (min-width: 1024px) {
  #app.pane-closed { grid-template-columns: 240px minmax(0, 1fr); }
}
@media (max-width: 1023px) and (min-width: 769px) {
  #app.pane-closed { grid-template-columns: minmax(0, 1fr); }
}
@media (min-width: 769px) {
  #app.pane-closed #comments-pane { display: none; }
}

@media (max-width: 1279px) and (min-width: 1024px) {
  #app { grid-template-columns: 240px minmax(0, 1fr) 320px; }
}

@media (max-width: 1023px) and (min-width: 769px) {
  #app { grid-template-columns: minmax(0, 1fr) 320px; }
  #files-pane {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: min(300px, 86vw);
    z-index: 60;
    transform: translateX(-105%);
    transition: transform 0.25s ease;
    box-shadow: var(--shadow-2);
  }
  #files-pane.open { transform: translateX(0); }
  #nav-btn { display: inline-flex; }
  #scrim { position: fixed; inset: 0; background: rgba(25, 24, 23, 0.34); z-index: 55; }

  /* The rail is a fixed drawer here, so it no longer occupies a grid track.
     Without this the needs list is placed in the abandoned rail column and
     renders in a 272px sliver with the rest of the window empty. */
  #app.needs-view { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 768px) {
  #app { display: block; height: 100dvh; }
  #doc-pane { height: 100dvh; }

  /* ---------- dialogs on a phone ----------
     Two separate faults, and together they made the members panel unusable:
     its Add button and the × beside each person were both unreachable.

     First, layering. Below this breakpoint the panes become drawers at
     z-index 60, while every dialog is a .floaty at 50 — so opening the
     members panel from the sidebar put it *behind* the sidebar. 65 clears the
     drawers and the scrim (55) while staying under #mention-menu and
     #drawio-modal (70), which are meant to sit above a dialog. */
  .floaty { z-index: 65; }

  /* Second, width. These carry fixed pixel widths — 420, 460, 500 — all wider
     than a 390-414px phone. Centred, a dialog wider than the screen loses
     *both* edges, and the right edge is where the remove and confirm controls
     live. */
  #members-form, #account-form, #usage-form, #new-doc-form, #agent-form,
  #repo-form, #link-form, #chat-move-form, #diagram-form, #ticket-form {
    width: min(420px, calc(100vw - 20px));
    max-width: calc(100vw - 20px);
  }

  /* Third, vertical placement. Most of these open at `top: 22%`, and capping
     them at 84vh put the bottom 6vh of the box below the fold — permanently,
     because a .floaty is fixed, so the page cannot be scrolled to reach it and
     scrolling *inside* the dialog only moves content within a box whose own
     foot is off-screen. On the agent panel that strip is the token list: a
     phone could mint tokens but never revoke one.

     Centred the same way the horizontal rule above does it — both insets 0 and
     auto margins — rather than with translate, which the pop-in animation
     owns. `height: fit-content` is what makes it work: with `height: auto` an
     element with both insets set stretches to fill instead of shrinking to its
     content, and auto margins would have nothing left to split. */
  #pr-form, #repo-form, #link-form, #agent-form, #chat-move-form,
  #members-form, #account-form, #usage-form, #new-doc-form, #diagram-form, #ticket-form {
    top: 0;
    bottom: 0;
    height: fit-content;
    margin-block: auto;
    max-height: 88vh;
    overflow-y: auto;
  }

  #nav-btn, #comments-btn {
    display: inline-flex;
    width: 40px;
    height: 40px;
    border-radius: var(--r-lg);
    background: var(--surface-sunken);
  }
  #comments-btn {
    background: var(--ink);
    color: var(--ink-accent);
    margin-left: auto;
  }
  #comments-btn .badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 18px;
    height: 18px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 4px;
    border-radius: var(--r-pill);
    background: var(--attn-chip);
    color: var(--attn-chip-text);
    font-size: 10.5px;
    font-weight: 700;
    box-shadow: 0 0 0 2px var(--bg);
  }
  #doc-head { padding: 10px 14px; gap: 10px; }
  .breadcrumb {
    flex: 1;
    min-width: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
  }
  .breadcrumb .sep { display: none; }
  .breadcrumb .crumb { font-size: 11px; }
  .breadcrumb .crumb.last { font-size: 13.5px; font-weight: 650; order: -1; max-width: 100%; }
  #doc-head #edit-btn { margin-left: 0; padding: 8px 10px; }
  #doc-head #edit-btn .btn-label { display: none; }
  #refresh-btn { display: none; }
  /* No room for a sentence beside a filename and four buttons on a phone, and
     the sentence is the point — so it takes a line of its own underneath. Last
     in order so the buttons keep the first row; the wrap only ever happens
     while the chip is on screen, since a hidden one is display:none. */
  #doc-head { flex-wrap: wrap; }
  .snap-chip { order: 10; flex-basis: 100%; margin-top: 2px; white-space: normal; }

  #doc-meta { padding: 18px 20px 0; }
  .markdown-body { padding: 22px 20px 130px; font-size: 18px; line-height: 1.7; }

  /*
   * A styled HTML document on a phone.
   *
   * The article's own padding shrinks like the Markdown one. The document's
   * side padding is then capped, and that one is a real override of somebody's
   * design rather than of their absence: `padding: 2rem 3rem` is a reasonable
   * thing to write for a page on a laptop and it leaves a 375px screen with
   * 231px of column, which is not a smaller version of the design — it is a
   * different and worse one. Vertical padding is left exactly as written,
   * because nothing about a phone makes the space above a heading wrong.
   *
   * A document that wrote its own narrow-screen rule lands near 16px anyway,
   * so this mostly changes the documents that never considered a phone.
   */
  #article.host-doc { padding: 22px 16px 130px; }

  /*
   * `white-space: nowrap` on inline code is a reasonable thing to write for a
   * desktop column — an identifier reads as one unit — and on a phone it is a
   * 544px word. Broken here rather than left to scroll, because a paragraph
   * that has to be panned to be read is not a paragraph.
   *
   * Inline code only. A <pre> block keeps its nowrap and scrolls, which is what
   * a code block is supposed to do.
   */
  #article.host-doc :not(pre) > code {
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .markdown-body h1 { font-size: 30px; }
  .markdown-body h2 { font-size: 22px; margin-top: 32px; }
  .markdown-body h3 { font-size: 18.5px; }
  /* The editorial pass added a drop cap, a byline, pull quotes and section
     rules, all sized for a 640px column. Left alone they arrived on a phone at
     desktop proportions: the cap ate a third of the line, and the byline's
     letterspaced caps wrapped with one word stranded. */
  .markdown-body > h1:first-of-type + p::first-letter { font-size: 2.6em; line-height: 0.86; margin-right: 0.06em; }
  .markdown-body h2 { padding-top: 16px; }
  .markdown-body blockquote { margin: 24px 0; padding-left: 16px; font-size: 1.05em; }
  .markdown-body hr { margin: 36px auto; width: 60%; }
  #doc-meta { font-size: 10px; letter-spacing: 0.06em; text-wrap: pretty; }

  /* files pane → left drawer */
  #files-pane {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: min(320px, 86vw);
    z-index: 60;
    transform: translateX(-105%);
    transition: transform 0.25s ease;
    box-shadow: var(--shadow-2);
  }
  #files-pane.open { transform: translateX(0); }

  /* comments pane → bottom sheet */
  #comments-pane {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    height: 78dvh;
    z-index: 60;
    transform: translateY(105%);
    transition: transform 0.28s ease;
    border-left: 0;
    border-top: 1px solid var(--border);
    border-radius: 22px 22px 0 0;
    box-shadow: var(--e4);
  }
  #comments-pane.open { transform: translateY(0); }
  #comments-pane::before {
    content: "";
    display: block;
    width: 40px;
    height: 4px;
    border-radius: 2px;
    background: var(--border-hover);
    margin: 8px auto 0;
    flex: none;
  }
  .pane-sticky { border-radius: 22px 22px 0 0; }
  .pane-title { font-size: 16px; }
  .chip { font-size: 13px; padding: 8px 13px; }
  #threads { padding-bottom: calc(20px + env(safe-area-inset-bottom)); }
  .msg .body { font-size: 14.5px; line-height: 1.55; }
  .text-btn { padding: 8px 10px; font-size: 12.5px; }
  .reply-input { padding: 10px 12px; font-size: 14px; }
  .reply-row .icon-btn, .thread-foot .icon-btn { width: var(--tap); height: var(--tap); }

  #scrim {
    position: fixed;
    inset: 0;
    background: rgba(25, 24, 23, 0.32);
    z-index: 55;
  }

  /* Keep the depth. This flattened every file to a fixed 22px indent, so on a
     phone a document three folders deep sat level with one at the root and the
     tree stopped being a tree — and it lands after the rest of the mobile rules
     for this pane, so it was quietly winning. Vertical padding sets the row
     height; nothing needs a min-height on top of it. */
  #file-list .file {
    padding: 9px 10px 9px calc(22px + var(--tree-base, 0px) + min(var(--depth, 0) * 12px, 84px));
    font-size: 14px;
    min-height: 0;
  }
  /* Except inside the open document's card, which already carries the depth as
     its own margin. The rule above is an id plus a class and outspecifies the
     `.file-card .file` that guards against this on desktop, so without repeating
     the guard here at matching weight the indent is applied twice and the open
     document sits 36px deeper than the siblings it is filed beside. */
  #file-list .file-card .file { padding-left: 22px; }
  .fy-row { padding: 11px 9px; }

  /* selection bar docks above the thumb */
  #sel-bar {
    left: 20px !important;
    right: 20px;
    top: auto !important;
    bottom: 74px;
    border-radius: var(--r-2xl);
    padding: 5px;
  }
  #sel-bar .sel-item { flex: 1; height: var(--tap); justify-content: center; font-size: 14px; }
  #sel-bar .sel-icon { width: var(--tap); }

  /* composer docks as a bottom sheet above the keyboard */
  #composer.sheet {
    left: 0 !important;
    right: 0;
    top: auto !important;
    bottom: 0;
    width: auto;
    border-radius: 22px 22px 0 0;
    padding-bottom: calc(13px + env(safe-area-inset-bottom));
    animation: sheet-up 0.22s ease;
  }
  #composer.sheet textarea { font-size: 16px; }

  /* iOS Safari zooms the whole page whenever a focused field is under 16px,
     and does not zoom back out when the field is blurred. One tap on an email
     box therefore leaves the entire app enlarged and clipped, with no obvious
     way back.
     The editor, the search box and the composer were each pinned to 16px for
     exactly this reason; the other nineteen fields — every dialog, the repo
     form, the member invite, the delete-account confirmation, the Ask box —
     were not, so the guard only held wherever somebody had remembered it.
     Here it is once, for all of them.

     The selectors are deliberately over-specified: the rules being beaten set
     font-size through id selectors, and a media query adds no specificity of
     its own, so an element selector here would simply lose. */
  #account-form input, #account-form select, #members-form input, #chat-move-form input,
  #repo-form input, #new-doc-form input, #new-doc-form select,
  #link-form input, #agent-form input, #agent-form select,
  #diagram-form input:not([type='file']), #diagram-form textarea,
  #ticket-form input, #ticket-form textarea,
  .si-email-row input,
  #editor-wrap input, #editor-wrap textarea,
  #comments-pane input, #comments-pane textarea,
  #composer input, #composer textarea,
  .dg-row select, .reply-input {
    font-size: 16px;
  }
  #repo-form, #link-form, #agent-form { width: min(420px, 92vw); }
  #new-doc-form { width: min(460px, 92vw); top: 6%; max-height: 88vh; }
  #nd-templates { grid-template-columns: 1fr; }
  #rich-toolbar { padding: 6px 8px; }
  .rt-btn { min-width: 28px; padding: 0 5px; }
  #rich-editor { padding: 16px 16px 24px; }
  #mention-menu { max-width: calc(100vw - 16px); }

  /* first run */
  #welcome-pane { height: 100dvh; }
  .wp-body { grid-template-columns: minmax(0, 1fr); gap: 32px; padding: 28px 20px 60px; }
  .wp-left h1 { font-size: 28px; }
  .presence { display: none; }

  /* needs list */
  #needs-pane { height: 100dvh; }
  .needs-head { padding: 18px 20px 0; }
  .needs-head h1 { font-size: 24px; }
  #needs-list { padding: 16px 20px 40px; }
  .needs-filters { flex-wrap: wrap; }
  .ni-actions .reply-input { min-width: 100%; }

  /* editor: single column, panel stacks under the text */
  #editor-wrap { height: 100dvh; }
  #editor-bar { padding: 10px 14px; gap: 8px; }
  #editor-bar .ed-lead-text, #editor-bar .ed-promise { display: none; }
  #editor-body { display: flex; flex-direction: column; overflow-y: auto; }
  /* Grow to fill the screen. Without this the column is content-sized: the
     card took its 40vh and the remaining quarter of the phone sat empty below
     it, which on the one device where screen space is scarce is the wrong way
     round. `flex: 1 0 auto` grows into the space but never shrinks below the
     content, so the side panel still pushes the page into a normal scroll. */
  #editor-main {
    overflow: visible;
    padding: 16px 14px 0;
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
  }
  #editor-card { flex: 1; display: flex; flex-direction: column; min-height: 0; }
  #editor-card .ed-strip { flex: none; }
  #editor-side {
    border-left: 0;
    border-top: 1px solid var(--border);
    overflow: visible;
    padding: 20px 14px calc(40px + env(safe-area-inset-bottom));
  }
  /* 16px because anything smaller makes iOS zoom the page on focus. `flex: 1`
     so the writing area takes whatever the card has left; the 40vh floor stays
     as the minimum when the side panel is open and space is genuinely tight. */
  #editor { font-size: 16px; min-height: 40vh; flex: 1; }
}

@keyframes sheet-up {
  from { transform: translateY(30%); opacity: 0.5; }
  to { transform: none; opacity: 1; }
}

/* ============================================================================
   Toasts
   ========================================================================= */

#toasts {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--ink);
  color: var(--ink-text);
  border-radius: var(--r-lg);
  padding: 10px 16px;
  font-size: 13px;
  box-shadow: var(--shadow-2);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  max-width: 70vw;
  pointer-events: auto;
}
.toast.show { opacity: 1; transform: none; }
.toast.error { background: var(--danger); color: #fff; }
.toast .undo {
  border: 0;
  background: none;
  color: var(--ink-accent);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 650;
  cursor: pointer;
  padding: 0;
  flex: none;
}
.toast.error .undo { color: #fff; text-decoration: underline; }

/* ============================================================================
   Reduced motion
   ========================================================================= */

/*
 * Motion, for somebody who has asked their system for less of it.
 *
 * This used to name the animated things one selector at a time, which held for
 * exactly as long as nobody added an animation — and by now a dozen had got
 * past it: the presence chips, the search panel, half the dialogs, the sheet
 * grid, and every chart on the analytics page, which drew its lines and faded
 * its areas in on each load with no way to opt out. Turning the default around
 * costs nothing to maintain: everything is still, and the exceptions are named
 * instead. Chart entrances collapse to their finished state, so the data is
 * readable immediately rather than performed.
 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* The exceptions, all of them saying "this is still happening". Removing
     them removes the only report that anything is; they slow down instead. */
  .repo-refresh.spinning .ic,
  .analytics-spinner {
    animation-duration: 1.6s !important;
    animation-iteration-count: infinite !important;
  }
  #att-mic-btn.recording,
  .reply-row .icon-btn.recording {
    animation-duration: 2.4s !important;
    animation-iteration-count: infinite !important;
  }
  /* A blink taken to its first frame can land on the invisible half, and a
     caret you cannot see is worse than one that does not blink. */
  .chat-cursor { animation: none !important; opacity: 1; }
  mark.mdc-hl.pulse { animation: none !important; background: var(--hl-strong); }
}

/* ---- repository members ---- */
.member-list { list-style: none; margin: 0 0 14px; padding: 0; max-height: 15rem; overflow-y: auto; }
.member-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
}
.member-row:last-child { border-bottom: 0; }
.member-who { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.member-who b { font-size: 13px; font-weight: 600; }
.member-email {
  font-size: 12px;
  color: var(--text-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.member-tag {
  flex: none;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-soft);
  background: var(--bg-soft);
  border-radius: var(--r-pill);
  padding: 2px 9px;
}
/* Added, but nobody has signed in with that address yet — worth showing, so an
   owner can tell "they are here" from "they have not turned up". */
.member-waiting { background: var(--attn-bg); color: var(--text-mid); }
.member-remove {
  flex: none;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--text-soft);
  padding: 4px;
  border-radius: var(--r-md);
}
.member-remove:hover { color: var(--danger); background: var(--danger-bg); }
/* On a touch screen this is a thumb, not a cursor. 4px of padding around a
   15px glyph is a ~23px target — small enough to miss, and missing it means
   tapping the row beside it. The glyph stays the same size; only the reachable
   area grows, so nothing about the layout shifts on a desktop. */
@media (hover: none) {
  .member-remove {
    padding: 10px;
    margin: -3px -6px -3px 0;
  }
}
.member-add { margin-bottom: 10px; }
.member-add .row { gap: 8px; }
.member-add input { flex: 1; }

#members-form .member-add .row { justify-content: stretch; margin-top: 0; }
#members-form .member-add input { margin-bottom: 0; }

/* Requests waiting on the owner. Given the attention colour because they are
   the only thing in this panel that is asking for a decision. */
.member-queue { background: var(--attn-bg); border-radius: var(--r-md); padding: 8px 10px; margin-bottom: 12px; }
.member-queue h4, .member-quiet h4 {
  margin: 0 0 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-soft);
}
.member-queue .member-row, .member-quiet .member-row { border-bottom: 0; }
.member-quiet { opacity: 0.75; margin-top: 4px; }
.req-yes, .req-no, .req-clear {
  flex: none;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: var(--r-pill);
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text);
}
.req-yes { background: var(--accent); color: var(--on-accent); border-color: transparent; }
.req-yes:hover { filter: brightness(1.08); }
.req-no:hover, .req-clear:hover { background: var(--bg-soft); }

/* An owner with a backlog of requests can make this panel taller than the
   window; the panel scrolls rather than running off the bottom. */
#members-form { max-height: 82vh; overflow-y: auto; top: 9%; }

/* An unread mention count reads as something to do, not as a statistic. */
.fy-row.has-unread .fy-count {
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--r-pill);
  padding: 1px 7px;
  font-weight: 700;
}

/* A repository whose credential has gone. Muted, not alarming: reading still
   works, which is most of what this product is for. */
.repo-readonly {
  flex: none;
  font-size: 10px;
  font-weight: 650;
  letter-spacing: 0.02em;
  color: var(--text-soft);
  background: var(--bg-soft);
  border-radius: var(--r-xs);
  padding: 1px 5px;
}

/* ---- your data ---- */
#account-form { max-height: 84vh; overflow-y: auto; top: 8%; }

/* ---------- assistant usage ---------- */

#usage-form { max-height: 84vh; overflow-y: auto; top: 10%; }
.usage-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 10px; }
.usage-big {
  font-size: 34px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.usage-unit { font-size: 13px; color: var(--text-soft); }
/* The bar is the whole point of having an allowance: a number on its own does
   not say whether it is a lot. Without a ceiling there is nothing to fill, so
   the note replaces it rather than showing an empty trough that reads as
   zero. */
.usage-bar {
  height: 7px;
  border-radius: 4px;
  background: var(--surface-sunken);
  overflow: hidden;
}
.usage-bar-fill {
  display: block;
  height: 100%;
  border-radius: 4px;
  background: var(--accent);
  transition: width 0.3s ease;
}
.usage-bar-fill.warm { background: var(--hl-strong); }
.usage-bar-fill.hot { background: var(--danger); }
.usage-meter-note {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}
.usage-ownkey {
  margin-top: 10px;
  padding: 8px 10px;
  border-radius: var(--r-md);
  background: var(--accent-tint);
  color: var(--text-mid);
  font-size: 12.5px;
}
.usage-stats {
  display: flex;
  gap: 16px;
  margin: 12px 0 2px;
  font-size: 12.5px;
  color: var(--text-soft);
}
.usage-stats b { color: var(--text); font-weight: 650; font-variant-numeric: tabular-nums; }
.usage-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.usage-table th {
  text-align: left;
  font-weight: 600;
  color: var(--text-faint);
  padding: 0 0 6px;
  border-bottom: 1px solid var(--border);
}
.usage-table th:not(:first-child), .usage-table td:not(:first-child) {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.usage-table td { padding: 6px 0; border-bottom: 1px solid var(--border-soft); color: var(--text-mid); }
.usage-table tr:last-child td { border-bottom: none; }
.usage-table td:first-child { color: var(--text); }
.usage-warn { color: var(--danger); }

.acct-block { padding: 14px 0; border-top: 1px solid var(--border); }
.acct-block h4 { margin: 0 0 4px; font-size: 13px; font-weight: 650; }
.acct-note { margin: 0 0 12px; font-size: 12.5px; line-height: 1.55; color: var(--text-mid); }
/* Deletion is irreversible and reads that way — but only here, not as a
   red-tinged panel that makes the export above look dangerous too. */
.acct-danger h4 { color: var(--danger); }
.acct-warn {
  padding: 9px 11px;
  border-radius: var(--r-md);
  background: var(--attn-bg);
  color: var(--text-mid);
}
.acct-delete-btn {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 8px 14px;
  border-radius: var(--r-lg);
  border: 1px solid var(--danger);
  background: transparent;
  color: var(--danger);
  cursor: pointer;
}
.acct-delete-btn:hover:not(:disabled) { background: var(--danger); color: #fff; }
.acct-delete-btn:disabled { opacity: 0.45; cursor: not-allowed; border-color: var(--border); color: var(--text-soft); }

/* ---------- your own model key ---------- */

/* A saved key has no value to display — it is never sent back — so the state
   line carries the whole message, and a dot does the work a masked string of
   dots would only pretend to do. */
.acct-key-state {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
  font-size: 12.5px;
  color: var(--text-mid);
}
.acct-key-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ok);
}
/* Left-aligned, unlike the dialog's own footer row: these act on the block
   above them rather than closing the dialog, and sitting where Close sits
   would suggest otherwise. Qualified by the id because the rule it has to beat
   — `#account-form .row { justify-content: flex-end }` — carries one. */
#account-form .acct-key-row {
  display: flex;
  gap: 8px;
  margin-top: 12px;
  justify-content: flex-start;
}
#account-form .acct-key-row .primary,
#account-form .acct-key-row .ghost-btn { font-size: 13px; padding: 8px 14px; }
/* --- the addresses on an account --- */
.acct-emails { list-style: none; margin: 0 0 12px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.acct-emails li {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-soft, transparent);
}
/* The address takes the slack, so the tag and Remove sit hard right however
   long it is — and a long address truncates rather than wrapping the row. */
.acct-emails .acct-email-addr { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-email-tag {
  flex: none;
  font-size: 11px;
  letter-spacing: 0.02em;
  color: var(--text-mid);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 8px;
}
#account-form .acct-email-row { display: flex; gap: 8px; margin-top: 0; }
/* The input takes the room; the button keeps its own width. Without the
   flex:none the button squeezes to its text on a narrow dialog. */
#account-form .acct-email-row input { flex: 1; min-width: 0; }
#account-form .acct-email-row .ghost-btn { flex: none; font-size: 13px; padding: 8px 14px; }
/* Compact enough to sit inside a list row without setting its height. */
.acct-emails .acct-email-remove { flex: none; padding: 4px 9px; font-size: 12px; }
.acct-email-hint { margin-top: 8px; margin-bottom: 0; }

.ghost-btn.danger { color: var(--danger); border-color: var(--border); }
.ghost-btn.danger:hover:not(:disabled) { background: var(--danger-bg); border-color: var(--danger); }
.acct-key-msg { margin: 8px 0 0; }
#acct-key-provider {
  width: 100%;
  font: inherit;
  font-size: 13.5px;
  padding: 8px 10px;
  margin-bottom: 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm, 7px);
  background: var(--card);
  color: var(--text);
}
/* Monospace, because what goes in here is a machine string: a transposed
   character in a proportional font is the kind of thing you look straight
   past, and this is a field nobody can re-read once it is saved. */
#acct-key-value { font-family: var(--font-mono); letter-spacing: 0.02em; }

/* ---------- analytics dashboard ---------- */
/* Ported from the single-tenant portal. Colours come from the tokens above,
   so it follows the theme like everything else. */
#analytics-pane { padding: 1.5rem max(2rem, (100% - var(--page-max)) / 2) 3rem; width: 100%; overflow-y: auto; height: 100vh; max-height: 100vh; min-height: 0; }
/* Page titles take the document voice, the same as .needs-head h1 and the
   article's own H1. These two screens were built later in a different idiom
   — rem units, one-line rules — and hardcoded the UI sans, so the product
   had two conventions for the same thing: the For-you queue titled itself in
   one face and Tasks in another. Weight drops to 600 because the serif at
   700 sets much heavier than the sans did at the same number. */
#analytics-pane h1 { font-family: var(--font-doc); font-size: 1.45rem; font-weight: 600; margin: 0; }
#analytics-pane h2 { font-family: var(--font-ui); font-size: 0.75rem; font-weight: 600; margin: 0; color: var(--text-soft); text-transform: uppercase; letter-spacing: 0.06em; }
.analytics-head { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--border); }
.analytics-filters { display: flex; align-items: center; gap: 0.3rem; margin-left: auto; }
.analytics-filters .chip { padding: 0.25rem 0.6rem; border-radius: 5px; border: 1px solid var(--border); background: transparent; color: var(--text-soft); font-size: 0.75rem; cursor: pointer; font-family: var(--font-ui); font-weight: 500; transition: all 0.12s; }
.analytics-filters .chip:hover { border-color: var(--text-soft); color: var(--text); }
.analytics-filters .chip.active { background: var(--text); color: var(--bg); border-color: var(--text); }
.analytics-custom { display: flex; align-items: center; gap: 0.25rem; margin-left: 0.4rem; }
.analytics-dash { color: var(--text-soft); font-size: 0.7rem; }
.analytics-custom input[type="date"] { font-family: var(--font-ui); font-size: 0.7rem; padding: 0.2rem 0.35rem; border: 1px solid var(--border); border-radius: 4px; background: var(--card); color: var(--text); width: 105px; }
.analytics-loading { display: flex; align-items: center; justify-content: center; gap: 0.6rem; padding: 4rem; color: var(--text-soft); font-family: var(--font-ui); }
.analytics-spinner { width: 16px; height: 16px; border: 2px solid var(--border); border-top-color: var(--text-soft); border-radius: 50%; animation: a-spin 0.6s linear infinite; }
.analytics-empty { text-align: center; padding: 5rem 2rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-error { text-align: center; padding: 3rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-error p { margin-bottom: 0.5rem; }
.a-error p:first-child { color: var(--text); font-weight: 600; }
/* The sentence that says what to do about it. Bounded and centred so it reads
   as a paragraph rather than as one line stretched across the whole pane. */
.a-error-sub { max-width: 46ch; margin: 0 auto 0.9rem; font-size: 0.85rem; line-height: 1.55; }
.a-empty { color: var(--text-soft); font-size: 0.8rem; font-family: var(--font-ui); padding: 1.5rem; text-align: center; }
.a-muted { color: var(--text-soft); font-size: 0.75rem; font-family: var(--font-ui); margin-top: 0.5rem; }
/* Analytics panels are cards, and every other card in this product carries a
   token radius and the first step of elevation. These were flat outlines on a
   raw 10px radius, which is why the dashboard read as a different application
   from the pages either side of it. */
.analytics-section { background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); box-shadow: var(--e1); padding: 1rem 1.25rem; }
.analytics-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-bottom: 0.75rem; }
.a-chart-section { margin-bottom: 0.75rem; }
.analytics-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.6rem; margin-bottom: 0.75rem; }
.a-card { background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); box-shadow: var(--e1); padding: 0.85rem 1rem; display: flex; flex-direction: column; min-width: 0; }
.a-card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.4rem; }
.a-card-ic { color: var(--text-soft); }
.a-card-ic .ic { width: 16px; height: 16px; }
.a-card-val { font-size: 1.6rem; font-weight: 700; font-family: var(--font-ui); line-height: 1.1; color: var(--text); }
.a-card-label { font-size: 0.7rem; color: var(--text-soft); font-family: var(--font-ui); text-transform: uppercase; letter-spacing: 0.04em; margin-top: 0.15rem; }
.a-delta { font-size: 0.68rem; font-weight: 600; font-family: var(--font-ui); padding: 0.1rem 0.35rem; border-radius: 3px; }
.a-delta.up { color: var(--data-green-ink); background: color-mix(in srgb, var(--data-green) 14%, transparent); }
.a-delta.down { color: var(--data-red-ink); background: color-mix(in srgb, var(--data-red) 14%, transparent); }
.a-delta.new { color: var(--accent); background: rgba(79,70,214,0.08); }
.a-svg { width: 100%; height: auto; display: block; cursor: crosshair; }
.a-svg .ax { font-size: 9px; fill: var(--text-soft); font-family: var(--font-ui); }
.a-tooltip { position: absolute; pointer-events: none; background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 0.5rem 0.7rem; font-family: var(--font-ui); font-size: 0.75rem; line-height: 1.6; box-shadow: var(--e2); z-index: 10; white-space: nowrap; }
.analytics-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-bottom: 0.75rem; }
.a-doc-row { display: grid; grid-template-columns: 1fr auto; gap: 0.3rem; align-items: center; padding: 0.55rem 0.6rem; border-radius: 6px; text-decoration: none; color: var(--text); transition: background 0.1s; }
.a-doc-row:hover { background: var(--bg-soft); }
.a-doc-info { min-width: 0; }
.a-doc-name { font-weight: 600; font-size: 0.82rem; font-family: var(--font-ui); display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.a-doc-path { font-size: 0.68rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-doc-nums { display: flex; gap: 0.5rem; font-size: 0.72rem; color: var(--text-soft); font-family: var(--font-mono); }
.a-doc-contributors { font-weight: 600; }
.a-bar { grid-column: 1 / -1; height: 2px; background: var(--border); border-radius: 1px; overflow: hidden; margin-top: 0.15rem; }
.a-bar.sm { width: 60px; height: 3px; }
.a-bar-fill { height: 100%; border-radius: 1px; transition: width 0.25s ease; }
.a-bar-fill.threads { background: var(--data-amber); }
.a-bar-fill.replies { background: var(--data-blue); }
.a-person-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.45rem 0.4rem; border-radius: 6px; transition: background 0.1s; }
.a-person-row:hover { background: var(--bg-soft); }
.a-avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--bg-soft); border: 1.5px solid var(--border); display: flex; align-items: center; justify-content: center; font-size: 0.72rem; font-weight: 700; font-family: var(--font-ui); color: var(--text-soft); flex-shrink: 0; transition: border-color 0.15s; }
.a-avatar.online { border-color: var(--data-green); box-shadow: 0 0 0 2px color-mix(in srgb, var(--data-green) 22%, transparent); }
.a-person-info { flex: 1; min-width: 0; }
.a-person-name { font-size: 0.8rem; font-weight: 600; font-family: var(--font-ui); display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.a-person-sub { font-size: 0.68rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-person-total { font-size: 0.78rem; font-weight: 700; font-family: var(--font-ui); color: var(--text); min-width: 20px; text-align: right; }
.a-active-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.4rem 0.3rem; }
.a-active-info { flex: 1; min-width: 0; }
.a-active-name { font-size: 0.8rem; font-weight: 600; font-family: var(--font-ui); display: block; }
.a-active-time { font-size: 0.68rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-active-actions { font-size: 0.72rem; color: var(--text-soft); font-family: var(--font-mono); }
.a-dbar { display: flex; height: 6px; border-radius: 3px; overflow: hidden; background: var(--border); }
.a-dbar-seg { min-width: 2px; transition: width 0.3s ease; }
.a-dlegend { display: flex; gap: 1rem; margin-top: 0.5rem; font-size: 0.72rem; font-family: var(--font-ui); color: var(--text-soft); }
.a-ddot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 0.25rem; vertical-align: middle; }
.a-dmeta { margin-top: 0.5rem; font-size: 0.72rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-repo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 0.5rem; }
.a-repo-card { border: 1px solid var(--border); border-radius: 8px; padding: 0.75rem; text-align: left; background: transparent; cursor: pointer; font-family: var(--font-ui); transition: all 0.12s; display: flex; flex-direction: column; gap: 0.2rem; }
.a-repo-card:hover { border-color: var(--text-soft); background: var(--bg-soft); }
.a-repo-card.active { border-color: var(--accent); background: rgba(79,70,214,0.06); }
.a-repo-name { font-size: 0.82rem; font-weight: 700; }
.a-repo-stat { font-size: 0.7rem; color: var(--text-soft); }
.a-repo-rate { font-size: 0.68rem; color: var(--data-green-ink); font-weight: 600; }
.a-clear-filter { display: inline-flex; align-items: center; gap: 0.3rem; margin-top: 0.5rem; font-size: 0.75rem; font-family: var(--font-ui); color: var(--accent); background: rgba(79,70,214,0.06); border: 1px solid var(--accent); border-radius: 5px; padding: 0.25rem 0.6rem; cursor: pointer; }
.a-clear-filter:hover { background: rgba(79,70,214,0.12); }
#analytics-nav-btn[aria-pressed="true"] { background: var(--bg-soft); box-shadow: inset 0 0 0 1px var(--border-hover, var(--border)); }
.analytics-view #doc-pane { display: none; }
.analytics-view #comments-pane { display: none; }
.analytics-view #needs-pane { display: none; }
.analytics-view { grid-template-columns: var(--rail-w) minmax(0, 1fr) !important; overflow: hidden; }
@media (max-width: 900px) { .analytics-grid { grid-template-columns: 1fr; } }
@media (max-width: 768px) { #analytics-pane { padding: 1rem; } .analytics-cards { grid-template-columns: repeat(2, 1fr); } .analytics-head { flex-direction: column; align-items: flex-start; gap: 0.5rem; } .analytics-filters { margin-left: 0; } .a-repo-grid { grid-template-columns: 1fr; } }
@media (max-width: 1023px) and (min-width: 769px) { .analytics-view { grid-template-columns: minmax(0, 1fr) !important; } }
[data-theme="dark"] .a-card, [data-theme="dark"] .analytics-section, [data-theme="dark"] .a-repo-card { background: var(--card); border-color: var(--border); }
[data-theme="dark"] .a-tooltip { background: var(--card); border-color: var(--border); box-shadow: var(--e2); }
[data-theme="dark"] .analytics-custom input[type="date"] { background: var(--bg-soft); border-color: var(--border); color: var(--text); }
.a-tabs { display: flex; gap: 0; margin-bottom: 0.75rem; border-bottom: 1px solid var(--border); }
.a-tab { padding: 0.4rem 0.75rem; font-size: 0.75rem; font-family: var(--font-ui); font-weight: 500; color: var(--text-soft); background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer; transition: all 0.12s; }
.a-tab:hover { color: var(--text); }
.a-tab.active { color: var(--text); border-bottom-color: var(--text); }
.a-tab-content { display: none; }
.a-tab-content.active { display: block; }

.a-repo-select { font-family: var(--font-ui); font-size: 0.78rem; padding: 0.3rem 0.6rem; border: 1px solid var(--border); border-radius: 6px; background: var(--card); color: var(--text); cursor: pointer; max-width: 200px; appearance: none; -webkit-appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6' fill='%23666'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 8px center; padding-right: 24px; }
.a-repo-select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px rgba(79,70,214,0.1); }
[data-theme="dark"] .a-repo-select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6' fill='%23999'/%3E%3C/svg%3E"); }
.a-dec-stats { display: flex; gap: 1.5rem; margin-bottom: 0.6rem; }
.a-dec-stat { display: flex; flex-direction: column; }
.a-dec-num { font-size: 1.4rem; font-weight: 700; font-family: var(--font-ui); line-height: 1.1; }
.a-dec-lbl { font-size: 0.66rem; color: var(--text-soft); font-family: var(--font-ui); text-transform: uppercase; letter-spacing: 0.03em; }
.a-dec-bar { display: flex; height: 6px; border-radius: 3px; overflow: hidden; background: var(--border); }
.a-dec-seg { min-width: 2px; transition: width 0.4s ease; }
.a-live-badge { font-size: 0.65rem; background: var(--data-green); color: var(--on-data-green); border-radius: 99px; padding: 0.1rem 0.4rem; margin-left: 0.4rem; font-weight: 700; }
.a-live-badge.hidden { display: none; }
.a-live-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.35rem 0.2rem; }
.a-live-info { flex: 1; min-width: 0; }
.a-live-name { font-size: 0.8rem; font-weight: 600; font-family: var(--font-ui); display: block; }
.a-live-file { font-size: 0.7rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-live-repo { opacity: 0.7; }
.a-repo-bars { display: flex; flex-direction: column; gap: 0.5rem; }
.a-repo-bar-row { display: grid; grid-template-columns: 150px 1fr 40px; align-items: center; gap: 0.6rem; padding: 0.3rem 0; border-radius: 6px; transition: background 0.1s; }
.a-repo-bar-row:hover { background: var(--bg-soft); }
.a-repo-bar-name { font-size: 0.8rem; font-weight: 600; font-family: var(--font-ui); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.a-repo-bar-track { display: flex; height: 8px; border-radius: 4px; overflow: hidden; background: var(--border); }
.a-repo-bar-seg { height: 100%; transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1); }
.a-repo-bar-seg.threads { background: var(--data-amber); border-radius: 4px 0 0 4px; }
.a-repo-bar-seg.replies { background: var(--data-blue); border-radius: 0 4px 4px 0; }
.a-repo-bar-num { font-size: 0.78rem; font-weight: 700; font-family: var(--font-ui); text-align: right; color: var(--text); }
.a-repo-bar-legend { display: flex; gap: 1rem; margin-top: 0.4rem; font-size: 0.72rem; font-family: var(--font-ui); color: var(--text-soft); }
.a-repo-bar-row.active { background: rgba(79,70,214,0.06); }
.a-repo-bar-row.active .a-repo-bar-name { color: var(--accent); }
.a-scroll-section { display: flex; flex-direction: column; max-height: 340px; min-height: 120px; }
.a-scroll-section .section-head { flex-shrink: 0; }
.a-scroll-body { overflow-y: auto; flex: 1; min-height: 0; padding-right: 0.25rem; }
.a-scroll-body::-webkit-scrollbar { width: 4px; }
.a-scroll-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
.a-chart-section .section-head { display: flex; align-items: center; justify-content: space-between; }
.a-chart-detail { margin-top: 0.75rem; padding: 0.75rem 1rem; background: var(--bg-soft); border-radius: 8px; border: 1px solid var(--border); animation: a-slideDown 0.2s ease; }
.a-detail-head { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.5rem; }
.a-detail-head b { font-family: var(--font-ui); font-size: 0.85rem; }
.a-detail-total { font-size: 0.75rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-detail-close { margin-left: auto; background: none; border: none; cursor: pointer; color: var(--text-soft); font-size: 0.9rem; padding: 0.2rem 0.4rem; border-radius: 4px; }
.a-detail-close:hover { background: var(--border); }
.a-detail-grid { display: flex; gap: 1.5rem; }
.a-detail-item { text-align: center; }
.a-detail-val { display: block; font-size: 1.3rem; font-weight: 700; font-family: var(--font-ui); }
.a-detail-lbl { font-size: 0.7rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-detail-files { margin-top: 0.6rem; border-top: 1px solid var(--border); padding-top: 0.5rem; }
.a-detail-files h4 { font-size: 0.7rem; font-weight: 600; font-family: var(--font-ui); color: var(--text-soft); text-transform: uppercase; letter-spacing: 0.04em; margin: 0 0 0.4rem; }
.a-detail-file { display: flex; align-items: center; justify-content: space-between; padding: 0.25rem 0; }
.a-df-name { font-size: 0.8rem; font-weight: 600; font-family: var(--font-ui); }
.a-df-meta { font-size: 0.7rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-hint { font-size: 0.68rem; color: var(--text-soft); font-family: var(--font-ui); opacity: 0.7; margin-left: auto; }
.a-repo-block { border-bottom: 1px solid var(--border-soft, var(--border)); }
.a-repo-block:last-child { border-bottom: none; }
.a-repo-row { display: grid; grid-template-columns: 14px 1fr 120px 40px; align-items: center; gap: 0.6rem; padding: 0.55rem 0.3rem; cursor: pointer; border-radius: 6px; transition: background 0.1s; }
.a-repo-row:hover { background: var(--bg-soft); }
.a-repo-row.active { background: rgba(79,70,214,0.06); }
.a-repo-caret { font-size: 0.6rem; color: var(--text-soft); transition: transform 0.15s; }
.a-repo-meta { min-width: 0; display: flex; flex-direction: column; }
.a-repo-name { font-size: 0.82rem; font-weight: 600; font-family: var(--font-ui); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.a-repo-sub { font-size: 0.68rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-repo-docs { padding: 0.35rem 0 0.5rem 1.7rem; }
.a-repo-doc { display: grid; grid-template-columns: 1fr auto; gap: 0.25rem; align-items: center; padding: 0.4rem 0.5rem; border-radius: 6px; text-decoration: none; color: var(--text); transition: background 0.1s; }
.a-repo-doc:hover { background: var(--bg-soft); }
.a-rd-info { min-width: 0; }
.a-rd-name { font-size: 0.78rem; font-weight: 600; font-family: var(--font-ui); display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.a-rd-dir { font-size: 0.66rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-rd-nums { display: flex; gap: 0.45rem; font-size: 0.7rem; color: var(--text-soft); font-family: var(--font-mono); }
.a-stuck-row { display: flex; align-items: flex-start; gap: 0.7rem; padding: 0.55rem 0.4rem; border-radius: 6px; text-decoration: none; color: var(--text); transition: background 0.1s; }
.a-stuck-row:hover { background: var(--bg-soft); }
.a-stuck-age { flex-shrink: 0; min-width: 38px; text-align: center; font-size: 0.68rem; font-weight: 700; font-family: var(--font-ui); padding: 0.15rem 0.35rem; border-radius: 4px; background: var(--border); color: var(--text-soft); }
.a-stuck-age.warm { background: color-mix(in srgb, var(--data-amber) 18%, transparent); color: var(--data-amber-ink); }
.a-stuck-age.hot { background: color-mix(in srgb, var(--data-red) 18%, transparent); color: var(--data-red-ink); }
.a-stuck-info { min-width: 0; flex: 1; }
.a-stuck-text { font-size: 0.8rem; font-family: var(--font-ui); display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.a-stuck-meta { font-size: 0.68rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-resp-top { display: flex; gap: 1.25rem; margin-bottom: 0.7rem; }
.a-resp-stat { display: flex; flex-direction: column; }
.a-resp-val { font-size: 1.4rem; font-weight: 700; font-family: var(--font-ui); line-height: 1.1; }
.a-resp-val.warn { color: var(--data-red-ink); }
.a-resp-lbl { font-size: 0.66rem; color: var(--text-soft); font-family: var(--font-ui); text-transform: uppercase; letter-spacing: 0.03em; }
.a-resp-track { height: 6px; background: var(--border); border-radius: 3px; overflow: hidden; }
.a-resp-fill { height: 100%; background: var(--data-green); border-radius: 3px; transition: width 0.5s cubic-bezier(0.4,0,0.2,1); }
.a-backlog { display: flex; flex-direction: column; gap: 0.45rem; }
.a-bl-row { display: grid; grid-template-columns: 78px 1fr 32px; align-items: center; gap: 0.5rem; }
.a-bl-label { font-size: 0.72rem; color: var(--text-soft); font-family: var(--font-ui); }
.a-bl-track { height: 7px; background: var(--border); border-radius: 4px; overflow: hidden; }
.a-bl-fill { height: 100%; border-radius: 4px; transition: width 0.5s cubic-bezier(0.4,0,0.2,1); }
.a-bl-num { font-size: 0.75rem; font-weight: 700; font-family: var(--font-ui); text-align: right; }
.a-search { width: 100%; font-family: var(--font-ui); font-size: 0.75rem; padding: 0.35rem 0.55rem; margin-bottom: 0.5rem; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-soft); color: var(--text); }
.a-search:focus { outline: none; border-color: var(--accent); }
.a-search::placeholder { color: var(--text-soft); }
.a-doc-controls { display: flex; align-items: center; gap: 8px; margin-bottom: 0.5rem; }
.a-doc-controls .a-search { flex: 1; margin: 0; }
.a-mini-select { font-family: var(--font-ui); font-size: 0.7rem; padding: 0.2rem 0.4rem; border: 1px solid var(--border); border-radius: 5px; background: var(--card); color: var(--text-soft); cursor: pointer; margin-left: auto; }
.a-mini-select:focus { outline: none; border-color: var(--accent); color: var(--text); }
.a-chips { display: flex; gap: 0.25rem; margin-left: auto; }
.a-chip-sm { font-family: var(--font-ui); font-size: 0.68rem; padding: 0.18rem 0.45rem; border: 1px solid var(--border); border-radius: 4px; background: transparent; color: var(--text-soft); cursor: pointer; transition: all 0.12s; }
.a-chip-sm:hover { border-color: var(--text-soft); color: var(--text); }
.a-chip-sm.active { background: var(--text); color: var(--bg); border-color: var(--text); }
.a-mini-btn { font-family: var(--font-ui); font-size: 0.68rem; padding: 0.18rem 0.5rem; border: 1px solid var(--border); border-radius: 4px; background: transparent; color: var(--text-soft); cursor: pointer; transition: all 0.12s; }
.a-mini-btn:hover { border-color: var(--accent); color: var(--accent); }
.a-nd-repos { margin-bottom: 0.75rem; padding-bottom: 0.6rem; border-bottom: 1px solid var(--border-soft, var(--border)); }
.a-nd-repo-row { display: grid; grid-template-columns: 120px 1fr 28px; align-items: center; gap: 0.5rem; padding: 0.2rem 0; }
.a-nd-repo-name { font-size: 0.72rem; font-weight: 600; font-family: var(--font-ui); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.a-nd-repo-bar { height: 5px; background: var(--border); border-radius: 3px; overflow: hidden; }
.a-nd-repo-fill { height: 100%; background: var(--accent); border-radius: 3px; transition: width 0.4s ease; }
.a-nd-repo-num { font-size: 0.7rem; font-weight: 700; font-family: var(--font-ui); text-align: right; color: var(--text); }
.a-nd-timeline { }
.a-nd-group { padding-left: 1rem; border-left: 2px solid var(--border); margin-bottom: 0.4rem; position: relative; }
.a-nd-group:last-child { margin-bottom: 0; }
.a-nd-dayhead { display: flex; align-items: center; gap: 0.4rem; margin-bottom: 0.15rem;
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--card);
}
/* An inline flex child, not a pin on the rail: absolute positioning fought
   the sticky header for an ancestor and lost twice. The rail stays the
   timeline's spine; the dot rides the label, aligned by layout. */
.a-nd-daydot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
.a-nd-daylabel { font-size: 0.73rem; font-weight: 600; font-family: var(--font-ui); color: var(--text); }
.a-nd-daycount { font-size: 0.65rem; color: var(--text-soft); font-family: var(--font-ui); background: var(--bg-soft); padding: 0.05rem 0.35rem; border-radius: 3px; }
.a-nd-file { display: flex; align-items: center; justify-content: space-between; padding: 0.25rem 0.4rem; border-radius: 4px; text-decoration: none; color: var(--text); transition: background 0.1s; }
.a-nd-file:hover { background: var(--bg-soft); }
.a-nd-fname { font-size: 0.75rem; font-weight: 500; font-family: var(--font-ui); }
.a-nd-frepo { font-size: 0.65rem; color: var(--text-soft); font-family: var(--font-ui); }
.analytics-content .analytics-grid { animation: a-fadeUp 0.35s ease-out both; }
.analytics-content .analytics-cards { animation-delay: 0s; }
.analytics-content .analytics-section:nth-child(2) { animation-delay: 0.05s; }
.analytics-content .analytics-section:nth-child(3) { animation-delay: 0.1s; }
.analytics-content .analytics-grid:nth-child(4) { animation-delay: 0.12s; }
.analytics-content .analytics-grid:nth-child(5) { animation-delay: 0.15s; }
.analytics-content .analytics-section:nth-child(n+6) { animation-delay: 0.18s; }
.a-svg { transition: opacity 0.3s ease; }
/* The chart RESTS visible. The draw-in lives entirely in the keyframes, so if
   a sweep ever prunes them again the failure is "no animation", never "no
   chart" — which is exactly what happened when these rules hid the resting
   state and the keyframes vanished. */
@keyframes a-draw { from { stroke-dashoffset: 2000; } to { stroke-dashoffset: 0; } }
@keyframes a-fadeIn { from { opacity: 0; } to { opacity: 1; } }
.a-line { stroke-dasharray: 2000; animation: a-draw 1s ease-out; }
.a-area { animation: a-fadeIn 0.6s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .a-line, .a-area { animation: none; }
}

/* The legend under the chart — these classes were emitted by renderChart and
   styled by nothing, so three series names ran together as one word. */
.chart-container { position: relative; }
.chart-legend {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 6px;
  font-size: 0.7rem;
  font-family: var(--font-ui);
  color: var(--text-soft);
}
.legend-i { display: inline-flex; align-items: center; gap: 5px; }
.leg-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.chart-hint { margin-left: auto; opacity: 0.7; }
.a-section-desc { font-size: 0.68rem; color: var(--text-soft); font-family: var(--font-ui); margin: 0 0 0.5rem; opacity: 0.8; }
.a-clickable { text-decoration: none; cursor: pointer; }
.a-clickable:hover { background: var(--bg-soft); }
.a-person-go { font-size: 0.8rem; color: var(--text-soft); opacity: 0; transition: opacity 0.15s; }
.a-clickable:hover .a-person-go { opacity: 1; }
.a-uchart { display: flex; flex-direction: column; gap: 0.35rem; }
.a-uchart-row { display: grid; grid-template-columns: 80px 1fr 32px; align-items: center; gap: 0.5rem; padding: 0.15rem 0; }
.a-uchart-name { font-size: 0.72rem; font-family: var(--font-ui); color: var(--text); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.a-uchart-bar { display: flex; height: 14px; border-radius: 4px; overflow: hidden; background: var(--border-soft, var(--border)); }
.a-uchart-seg { height: 100%; transition: width 0.4s ease; }
.a-uchart-seg.t { background: var(--data-amber); border-radius: 4px 0 0 4px; }
.a-uchart-seg.r { background: var(--data-blue); border-radius: 0 4px 4px 0; }
.a-uchart-num { font-size: 0.72rem; font-weight: 700; font-family: var(--font-ui); color: var(--text); text-align: right; }
.a-rd-header { display: flex; justify-content: space-between; padding: 0.2rem 0.5rem; margin-bottom: 0.2rem; font-size: 0.65rem; font-family: var(--font-ui); color: var(--text-soft); text-transform: uppercase; letter-spacing: 0.04em; border-bottom: 1px solid var(--border-soft, var(--border)); }

/* ---------- task tracker ---------- */
#tracker-pane { padding: 1.5rem max(2rem, (100% - var(--page-max)) / 2) 3rem; width: 100%; overflow-y: auto; height: 100vh; max-height: 100vh; min-height: 0; }
#tracker-pane h1 { font-family: var(--font-doc); font-size: 1.45rem; font-weight: 600; margin: 0; }
.tracker-head { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--border); }
.tracker-repo-box select, #tracker-repo { font-family: var(--font-ui); font-size: 0.8rem; padding: 0.3rem 0.5rem; border: 1px solid var(--border); border-radius: 6px; background: var(--card); color: var(--text); }
.tk-spacer { flex: 1; }
.tk-view-toggle { display: flex; gap: 0.25rem; }
.tk-view-toggle .chip, .tk-filters .chip { padding: 0.25rem 0.6rem; border-radius: 5px; border: 1px solid var(--border); background: transparent; color: var(--text-soft); font-size: 0.75rem; cursor: pointer; font-family: var(--font-ui); font-weight: 500; transition: all 0.12s; }
.tk-view-toggle .chip.active, .tk-filters .chip.active { background: var(--text); color: var(--bg); border-color: var(--text); }
.tk-filters { display: flex; align-items: center; gap: 0.3rem; flex-wrap: wrap; margin-bottom: 0.9rem; }
.tk-filters .chip b { font-weight: 700; opacity: 0.75; }
.tk-search { margin-left: auto; font-family: var(--font-ui); font-size: 0.78rem; padding: 0.3rem 0.55rem; border: 1px solid var(--border); border-radius: 6px; background: var(--card); color: var(--text); min-width: 180px; }
.tk-composer { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.6rem; border: 1px dashed var(--border-strong, var(--border)); border-radius: 8px; margin-bottom: 0.6rem; background: var(--bg-soft); }
.tk-composer-plus svg { width: 14px; height: 14px; color: var(--text-soft); display: block; }
#tk-new-title { flex: 1; border: none; background: transparent; color: var(--text); font-family: var(--font-ui); font-size: 0.85rem; padding: 0.25rem 0; outline: none; }

/* One primary action per screen. Import CSV, Export CSV and JSON held the most
   prominent corner of this page in full button chrome, while the thing the
   page exists for — adding a task — was a dashed grey row underneath them.
   Resting borderless demotes the three without moving them or making them any
   harder to reach; they take the same accent border as every other ghost
   button the moment you point at one. */
.tracker-head #tk-import-btn,
.tracker-head #tk-export-csv,
.tracker-head #tk-export-json { border-color: transparent; background: none; color: var(--text-quiet); }
.tracker-head #tk-import-btn:hover,
.tracker-head #tk-export-csv:hover,
.tracker-head #tk-export-json:hover { border-color: var(--accent); background: var(--card); color: var(--accent); }

/* …and the composer becomes what it actually is. Solid on the card surface
   rather than dashed on the sunken one, and carrying the focus ring its input
   gives away with outline:none — without this the primary control on the page
   showed a keyboard user nothing at all. */
.tk-composer { border-style: solid; border-color: var(--border); background: var(--card); }
.tk-composer:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint); }
.tk-headrow, .tk-row { display: grid; grid-template-columns: 110px minmax(200px, 1fr) 170px 96px 128px 128px 34px; gap: 0.5rem; align-items: center; }
.tk-headrow { padding: 0.15rem 0.6rem; font-size: 0.65rem; font-family: var(--font-ui); color: var(--text-soft); text-transform: uppercase; letter-spacing: 0.05em; }
.tk-row { padding: 0.3rem 0.6rem; border-radius: 8px; border: 1px solid transparent; }
.tk-row:hover { background: var(--surface-hover, var(--bg-soft)); }
.tk-row.done .tk-title { text-decoration: line-through; opacity: 0.55; }
.tk-title { text-align: left; background: none; border: none; cursor: pointer; font-family: var(--font-ui); font-size: 0.85rem; color: var(--text); padding: 0.2rem 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tk-has-desc { color: var(--text-faint, var(--text-soft)); margin-left: 0.35rem; font-size: 0.75rem; }
.tk-status, .tk-priority { font-family: var(--font-ui); font-size: 0.74rem; padding: 0.22rem 0.35rem; border-radius: 6px; border: 1px solid var(--border); background: var(--card); color: var(--text); }
.tk-status.s-done { color: var(--text-soft); }
.tk-status.s-blocked { border-color: var(--data-red); }
.tk-priority.p-urgent { border-color: var(--data-red); color: var(--data-red-ink); }
.tk-priority.p-high { border-color: var(--data-amber); }
.tk-priority.p-low { color: var(--text-soft); }
.tk-assignee { font-family: var(--font-ui); font-size: 0.76rem; padding: 0.24rem 0.4rem; border: 1px solid var(--border); border-radius: 6px; background: var(--card); color: var(--text); min-width: 0; }
.tk-date { font-family: var(--font-ui); font-size: 0.74rem; padding: 0.2rem 0.3rem; border: 1px solid var(--border); border-radius: 6px; background: var(--card); color: var(--text); min-width: 0; }
.tk-date.overdue { border-color: var(--data-red); color: var(--data-red-ink); }
.tk-row.overdue { border-color: color-mix(in srgb, var(--data-red) 45%, transparent); }
.tk-del { background: none; border: none; cursor: pointer; color: var(--text-faint, var(--text-soft)); border-radius: 6px; padding: 0.25rem; opacity: 0; transition: opacity 0.12s; }
.tk-row:hover .tk-del { opacity: 1; }
.tk-del:hover { color: var(--danger, #E06C5A); background: var(--danger-bg, transparent); }
.tk-del svg { width: 14px; height: 14px; display: block; }
.tk-detail { margin: 0 0.6rem 0.6rem; padding: 0.6rem; border: 1px solid var(--border); border-radius: 8px; background: var(--card); }
.tk-desc { width: 100%; min-height: 64px; resize: vertical; font-family: var(--font-mono); font-size: 0.78rem; padding: 0.45rem 0.55rem; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); }
.tk-detail-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 0.4rem; gap: 0.5rem; }
.tk-meta { font-size: 0.68rem; color: var(--text-soft); font-family: var(--font-ui); }
.tk-desc-preview { margin-top: 0.6rem; padding-top: 0.5rem; border-top: 1px dashed var(--border); font-size: 0.82rem; }
.tk-none { padding: 2rem 0.6rem; color: var(--text-soft); font-family: var(--font-ui); font-size: 0.85rem; }
.tk-empty { padding: 2rem; color: var(--text-soft); }

/* timeline */
.tkt-scroll { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--card); padding-bottom: 0.4rem; }
.tkt-grid { display: grid; grid-template-columns: 220px repeat(var(--tkt-days), 24px); grid-auto-rows: minmax(26px, auto); align-items: center; min-width: max-content; padding: 0.5rem 0.75rem 0.5rem 0; }
.tkt-month { grid-row: 1; font-family: var(--font-ui); font-size: 0.68rem; font-weight: 600; color: var(--text-soft); white-space: nowrap; padding-left: 2px; border-left: 1px solid var(--border); }
.tkt-day { grid-row: 2; font-family: var(--font-ui); font-size: 0.6rem; color: var(--text-faint, var(--text-soft)); text-align: center; padding: 2px 0 4px; border-bottom: 1px solid var(--border-soft, var(--border)); }
.tkt-day.wkend { background: var(--bg-soft); }
.tkt-day.today { color: var(--accent); font-weight: 700; box-shadow: inset 0 -2px 0 var(--accent); }
.tkt-label { grid-column: 1; display: flex; align-items: baseline; gap: 0.45rem; padding: 0 0.6rem; overflow: hidden; position: sticky; left: 0; background: var(--card); z-index: 1; }
.tkt-label-text { font-family: var(--font-ui); font-size: 0.76rem; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tkt-owner { font-family: var(--font-ui); font-size: 0.62rem; color: var(--text-soft); white-space: nowrap; }
.tkt-bar { height: 14px; border: none; border-radius: 7px; cursor: pointer; background: var(--data-blue); opacity: 0.9; min-width: 14px; }
.tkt-bar:hover { opacity: 1; box-shadow: 0 0 0 2px var(--accent-tint, rgba(0,0,0,0.1)); }
.tkt-bar.p-urgent { background: var(--data-red); }
.tkt-bar.p-high { background: var(--data-amber); }
.tkt-bar.p-low { background: var(--data-green); }
.tkt-bar.done { opacity: 0.35; }
.tkt-bar.overdue { box-shadow: 0 0 0 2px var(--data-red); }
.tkt-unscheduled { margin-top: 1rem; }
.tkt-unscheduled h3 { font-family: var(--font-ui); font-size: 0.72rem; font-weight: 600; color: var(--text-soft); text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 0.4rem; }
.tkt-un-row { display: grid; grid-template-columns: minmax(200px, 1fr) 128px 128px; gap: 0.5rem; align-items: center; padding: 0.25rem 0.6rem; border-radius: 6px; }
.tkt-un-row:hover { background: var(--surface-hover, var(--bg-soft)); }
.tkt-un-title { font-family: var(--font-ui); font-size: 0.8rem; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* the tracker takes the centre like analytics does */
.tracker-view #doc-pane, .tracker-view #comments-pane, .tracker-view #needs-pane { display: none; }
.tracker-view { grid-template-columns: var(--rail-w) minmax(0, 1fr) !important; overflow: hidden; }
@media (max-width: 1023px) and (min-width: 769px) { .tracker-view { grid-template-columns: minmax(0, 1fr) !important; } }
@media (max-width: 768px) {
  #tracker-pane { padding: 1rem; }
  /* A column here gave every child a full-width row of its own, so Import CSV,
     Export CSV and JSON took 220px of a phone screen — three stacked rows of
     data plumbing standing between the reader and the task list. They are one
     group and belong on one line. Row-with-wrap puts the title and the view
     toggle on the first line and lets the three of them wrap together onto the
     second, and the spacer goes, since a spacer on a wrapped line claims the
     whole line for itself. */
  .tracker-head { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
  .tracker-head .tk-spacer { display: none; }
  .tracker-head #tk-import-btn { margin-left: 0; }
  .tk-headrow { display: none; }
  .tk-row { grid-template-columns: 100px minmax(0, 1fr) 34px; grid-auto-rows: auto; }
  .tk-assignee, .tk-priority, .tk-date { grid-column: span 1; }
  .tk-search { min-width: 0; width: 100%; margin-left: 0; }
}

/* new-document type toggle */
.nd-type { display: flex; gap: 0.3rem; margin: 0 0 0.75rem; }
.nd-type .chip { padding: 0.25rem 0.6rem; border-radius: 5px; border: 1px solid var(--border); background: transparent; color: var(--text-soft); font-size: 0.75rem; cursor: pointer; font-family: var(--font-ui); font-weight: 500; }
.nd-type .chip.active { background: var(--text); color: var(--bg); border-color: var(--text); }

/* ---------- spreadsheets ---------- */
.sheet-wrap { max-width: none; }
.sheet-tabs { display: flex; gap: 0.25rem; margin-bottom: 0.5rem; flex-wrap: wrap; }
.sheet-tab { font-family: var(--font-ui); font-size: 0.76rem; padding: 0.28rem 0.7rem; border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 7px 7px 0 0; background: var(--bg-soft); color: var(--text-soft); cursor: pointer; }
.sheet-tab.active { background: var(--card); color: var(--text); border-bottom-color: var(--accent); font-weight: 600; }
.sheet-scroll { overflow: auto; max-height: calc(100vh - 220px); border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--card); }
.sheet-grid { border-collapse: separate; border-spacing: 0; font-family: var(--font-mono); font-size: 0.78rem; min-width: max-content; }
.sheet-grid th { position: sticky; background: var(--bg-soft); color: var(--text-soft); font-family: var(--font-ui); font-weight: 600; font-size: 0.68rem; z-index: 2; }
.sheet-grid thead th { top: 0; padding: 0.3rem 0.5rem; border-bottom: 1px solid var(--border-strong, var(--border)); border-right: 1px solid var(--border-soft, var(--border)); text-align: center; min-width: 70px; }
.sheet-grid th.sh-row, .sheet-grid th.sh-corner { left: 0; min-width: 44px; text-align: right; padding: 0.25rem 0.5rem; border-right: 1px solid var(--border-strong, var(--border)); border-bottom: 1px solid var(--border-soft, var(--border)); }
.sheet-grid th.sh-corner { z-index: 3; top: 0; }
.sh-cell { padding: 0.25rem 0.5rem; border-right: 1px solid var(--border-soft, var(--border)); border-bottom: 1px solid var(--border-soft, var(--border)); white-space: pre-wrap; max-width: 340px; overflow: hidden; color: var(--text-body, var(--text)); position: relative; }
.sh-cell.num { text-align: right; font-variant-numeric: tabular-nums; }
.sh-cell:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
.sh-cell.sel { background: var(--accent-wash, var(--accent-tint, rgba(100,140,220,0.14))); }
.sh-fx { color: var(--text-faint, var(--text-soft)); font-size: 0.62rem; margin-right: 0.25rem; user-select: none; }
.sh-cell.mdc-hl-cell { background: var(--hl); box-shadow: inset 0 -2px 0 var(--hl-strong); cursor: pointer; }
.sh-cell.mdc-hl-cell.resolved { background: var(--hl-resolved, transparent); box-shadow: none; }
.sh-cell.mdc-hl-cell.active { background: var(--hl-strong); }
.sh-cell.mdc-hl-cell.pulse { animation: sheetPulse 1.2s ease-out; }
@keyframes sheetPulse { 0% { box-shadow: inset 0 0 0 3px var(--accent); } 100% { box-shadow: inset 0 -2px 0 var(--hl-strong); } }
.sh-cell.changed { background: var(--accent-tint, rgba(100,140,220,0.15)); box-shadow: inset 0 0 0 1px var(--accent); }
.sh-cell.conflicted { box-shadow: inset 0 0 0 2px var(--data-red); }
.sh-input { width: 100%; min-width: 60px; border: none; background: var(--bg); color: var(--text); font: inherit; padding: 0; outline: 2px solid var(--accent); outline-offset: -1px; }
.sheet-truncated { font-family: var(--font-ui); font-size: 0.75rem; color: var(--text-soft); margin: 0.5rem 0 0; }
.sheet-legacy-card { border: 1px dashed var(--border-strong, var(--border)); border-radius: 12px; padding: 1.5rem; background: var(--bg-soft); font-family: var(--font-ui); max-width: 560px; }
.sheet-legacy-card h2 { margin: 0 0 0.5rem; font-size: 1rem; }
.sheet-legacy-card p { margin: 0; font-size: 0.85rem; color: var(--text-body, var(--text)); line-height: 1.55; }
.sheet-savebar { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; padding: 0.5rem 0.7rem; margin-bottom: 0.6rem; border: 1px solid var(--accent); border-radius: var(--r-lg); background: var(--accent-wash, var(--bg-soft)); }
.ssb-lead { font-family: var(--font-ui); font-weight: 600; font-size: 0.8rem; color: var(--text); }
.ssb-count { font-family: var(--font-ui); font-size: 0.74rem; color: var(--text-soft); }
.ssb-spacer { flex: 1; }
#ssb-title, #ssb-desc { font-family: var(--font-ui); font-size: 0.78rem; padding: 0.3rem 0.5rem; border: 1px solid var(--border); border-radius: 6px; background: var(--card); color: var(--text); }
#ssb-title { min-width: 220px; }
.quote-lost.stale-cell .quote-lost-label { color: var(--attn-text, inherit); }
.file .sheet-glyph { margin-right: 0.3rem; opacity: 0.6; font-size: 0.8em; }
@media (max-width: 768px) { .sheet-scroll { max-height: calc(100vh - 160px); } .sheet-savebar { position: sticky; top: 0; z-index: 4; } }

/* dark theme */
[data-theme="dark"] .tkt-bar { opacity: 0.85; }
[data-theme="dark"] .sh-cell.changed { box-shadow: inset 0 0 0 1px var(--accent); }

/* tracker: assignee suggestions + the delete affordance in the editor */
.tk-people-menu { position: fixed; z-index: 70; min-width: 250px; max-width: 320px; max-height: 224px; overflow-y: auto; background: var(--card); border-radius: var(--r-xl, 12px); box-shadow: var(--shadow-2), 0 0 0 1px var(--border); padding: 0.25rem; }
.tkp-item { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; width: 100%; text-align: left; background: none; border: none; cursor: pointer; padding: 0.4rem 0.55rem; border-radius: 8px; }
.tkp-item.sel, .tkp-item:hover { background: var(--surface-hover, var(--bg-soft)); }
.tkp-name { font-family: var(--font-ui); font-size: 0.8rem; font-weight: 600; color: var(--text); }
.tkp-email { font-family: var(--font-ui); font-size: 0.68rem; color: var(--text-soft); }
.tk-delete-btn { color: var(--danger, #E06C5A); display: inline-flex; align-items: center; gap: 0.3rem; }
.tk-delete-btn svg { width: 13px; height: 13px; }
.tk-delete-btn:hover { background: var(--danger-bg, rgba(224,108,90,0.12)); }

/* tracker: rich notes editor + conversation */
.tk-toolbar { display: flex; align-items: center; gap: 0.2rem; margin-bottom: 0.4rem; flex-wrap: wrap; }
.tk-toolbar button { font-family: var(--font-ui); font-size: 0.72rem; padding: 0.22rem 0.5rem; border: 1px solid var(--border); border-radius: 6px; background: var(--card); color: var(--text); cursor: pointer; }
.tk-toolbar button:hover { background: var(--surface-hover, var(--bg-soft)); }
.tk-tb-div { width: 1px; height: 16px; background: var(--border); margin: 0 0.2rem; }
.tk-tb-spacer { flex: 1; }
.tk-ai-btn { border-color: var(--accent) !important; color: var(--accent) !important; }
.tk-rich { min-height: 84px; max-height: 320px; overflow-y: auto; padding: 0.55rem 0.65rem; border: 1px solid var(--border); border-radius: 8px; background: var(--bg); color: var(--text-body, var(--text)); font-size: 0.85rem; line-height: 1.55; }
.tk-rich:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.tk-rich:empty::before { content: attr(data-placeholder); color: var(--text-faint, var(--text-soft)); }
.tk-rich ul, .tk-rich ol { padding-left: 1.3rem; margin: 0.3rem 0; }
.tk-rich p { margin: 0.3rem 0; }
.tk-rich a { color: var(--accent); }
.tk-convo { margin-top: 0.7rem; border-top: 1px dashed var(--border); padding-top: 0.55rem; }
.tk-convo h4 { font-family: var(--font-ui); font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-soft); margin: 0 0 0.45rem; }
.tkc-count { font-weight: 700; color: var(--text); }
.tkc-none { font-family: var(--font-ui); font-size: 0.76rem; color: var(--text-soft); margin: 0 0 0.5rem; }
.tkc-msg { padding: 0.35rem 0.45rem; border-radius: 8px; margin-bottom: 0.25rem; }
.tkc-msg:hover { background: var(--surface-hover, var(--bg-soft)); }
.tkc-head { display: flex; align-items: center; gap: 0.4rem; }
.tkc-author { font-family: var(--font-ui); font-size: 0.76rem; color: var(--text); }
.tkc-age { font-family: var(--font-ui); font-size: 0.65rem; color: var(--text-soft); }
.tkc-ticket, .tkc-del { background: none; border: none; cursor: pointer; color: var(--text-faint, var(--text-soft)); opacity: 0; border-radius: 4px; padding: 0.1rem; }
.tkc-ticket { margin-left: auto; }
.tkc-msg:hover .tkc-ticket, .tkc-msg:hover .tkc-del { opacity: 1; }
.tkc-ticket svg, .tkc-del svg { width: 12px; height: 12px; display: block; }
.tkc-text { font-size: 0.82rem; line-height: 1.5; margin: 0.15rem 0 0 1.9rem; color: var(--text-body, var(--text)); }
.tkc-atts { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.3rem 0 0 1.9rem; }
.tkc-img { max-height: 120px; max-width: 220px; border-radius: 6px; border: 1px solid var(--border); display: block; }
.tkc-audio { display: flex; flex-direction: column; gap: 0.2rem; }
.tkc-audio audio { height: 32px; max-width: 260px; }
.tkc-transcript { font-family: var(--font-ui); font-size: 0.68rem; color: var(--text-soft); max-width: 280px; }
.tkc-file { font-family: var(--font-ui); font-size: 0.76rem; color: var(--accent); text-decoration: none; border: 1px solid var(--border); border-radius: 6px; padding: 0.25rem 0.5rem; }
.tkc-file:hover { background: var(--surface-hover, var(--bg-soft)); }
.tkc-composer { display: flex; align-items: center; gap: 0.35rem; margin-top: 0.4rem; }
.tkc-composer .tkc-text-input, .tkc-composer input.tkc-text { margin: 0; }
input.tkc-text { flex: 1; font-family: var(--font-ui); font-size: 0.8rem; padding: 0.4rem 0.55rem; border: 1px solid var(--border); border-radius: 8px; background: var(--card); color: var(--text); margin: 0; }
.tkc-composer .ghost-btn svg { width: 14px; height: 14px; display: block; }
.tkc-pending { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.tkc-chip { font-family: var(--font-ui); font-size: 0.7rem; color: var(--text-soft); border: 1px solid var(--border); border-radius: 999px; padding: 0.12rem 0.5rem; display: inline-flex; align-items: center; gap: 0.25rem; }
.tkc-chip button { background: none; border: none; cursor: pointer; color: var(--text-soft); padding: 0; font-size: 0.8rem; }
.tk-talk { display: inline-flex; align-items: center; gap: 0.15rem; margin-left: 0.4rem; font-family: var(--font-ui); font-size: 0.68rem; color: var(--text-soft); }
.tk-talk svg { width: 11px; height: 11px; }

/* ---------- task tracker v2: fixed chrome, grouped scroll ---------- */
#tracker-pane { display: flex; flex-direction: column; overflow: hidden; padding: 1.25rem max(2rem, (100% - var(--page-max)) / 2) 0; }
.tracker-head { flex: none; }
.tk-filters { flex: none; margin-bottom: 0.6rem; }
#tk-composer { flex: none; margin-bottom: 0.7rem; }
.tk-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: auto; padding-bottom: 2.5rem; scrollbar-gutter: stable; }
.tk-group-by { display: inline-flex; align-items: center; gap: 0.3rem; margin-left: 0.6rem; font-family: var(--font-ui); font-size: 0.72rem; color: var(--text-soft); }
.tk-group-by select { font-family: var(--font-ui); font-size: 0.74rem; padding: 0.22rem 0.4rem; border: 1px solid var(--border); border-radius: 6px; background: var(--card); color: var(--text); }

.tk-group { margin-bottom: 1.2rem; }
/* A status group opens a section, and takes the rule the For-you groups and the
   document's own H2 take. */
.tk-group-head { position: sticky; top: 0; z-index: 4; display: flex; align-items: center; gap: 0.5rem; width: 100%; text-align: left; background: var(--bg); border: none; cursor: pointer; padding: 0.9rem 0.2rem 0.5rem; }
.tk-group-head::before { content: ''; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: var(--border-soft); }
.tk-group:first-child .tk-group-head::before { display: none; }
.tk-chevron { color: var(--text-soft); font-size: 0.65rem; width: 1em; }
.tk-group-pill { font-family: var(--font-ui); font-size: 0.66rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; padding: 0.2rem 0.65rem; border-radius: 999px; color: #fff; }
.g-in_progress { background: var(--data-blue); color: var(--on-data-blue); }
.g-blocked { background: var(--data-red); color: var(--on-data-red); }
.g-todo { background: var(--data-amber); color: var(--on-data-amber); }
.g-done { background: var(--data-green); color: var(--on-data-green); }
.g-p-urgent { background: var(--data-red); color: var(--on-data-red); }
.g-p-high { background: var(--data-amber); color: var(--on-data-amber); }
.g-p-normal { background: var(--data-blue); color: var(--on-data-blue); }
.g-p-low { background: var(--data-green); color: var(--on-data-green); }
.g-overdue { background: var(--data-red); color: var(--on-data-red); }
.g-plain { background: var(--bg-soft); color: var(--text); border: 1px solid var(--border); }
.tk-group-count { font-family: var(--font-ui); font-size: 0.7rem; font-weight: 600; color: var(--text-soft); }

/* rows: hairline separated, titles wrap to two lines instead of clipping */
.tk-headrow { padding: 0.1rem 0.6rem 0.25rem; }
.tk-row { border-radius: 0; border: none; border-bottom: 1px solid var(--border-soft, var(--border)); padding: 0.35rem 0.6rem; }
.tk-row:hover { background: var(--surface-hover, var(--bg-soft)); }
.tk-title { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.35; }
.tk-group-add { border: 1px dashed transparent; border-radius: 8px; margin-top: 0.15rem; }
.tk-group-add input { width: 100%; border: none; background: transparent; color: var(--text); font-family: var(--font-ui); font-size: 0.8rem; padding: 0.4rem 0.6rem; outline: none; }
.tk-group-add input::placeholder { color: var(--text-faint, var(--text-soft)); }
.tk-group-add:focus-within { border-color: var(--border-strong, var(--border)); background: var(--bg-soft); }

@media (min-width: 769px) {
  .tk-headrow, .tk-row { min-width: 900px; }
  .tk-group-add, .tk-none { min-width: 900px; }
}
@media (max-width: 768px) {
  #tracker-pane { padding: 1rem 1rem 0; }
  .tk-group-head { top: 0; }
}

/* tracker: many hands on a task, and @ in the conversation */
.tk-people-cell { display: inline-flex; align-items: center; gap: 0.35rem; background: none; border: none; cursor: pointer; padding: 0.15rem 0.3rem; border-radius: 6px; font-family: var(--font-ui); font-size: 0.76rem; color: var(--text); min-width: 0; max-width: 100%; }
.tk-people-cell:hover { background: var(--surface-hover, var(--bg-soft)); }
.tk-people-cell.empty { color: var(--text-faint, var(--text-soft)); font-style: italic; }
.tk-avatars { display: inline-flex; }
.tk-avatars > * { margin-left: -6px; }
.tk-avatars > *:first-child { margin-left: 0; }
.tk-people-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tk-people-more { font-size: 0.68rem; font-weight: 700; color: var(--text-soft); background: var(--bg-soft); border-radius: 999px; padding: 0.05rem 0.35rem; }
.tk-people-edit { display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem; margin: 0.55rem 0 0.15rem; }
.tk-people-label { font-family: var(--font-ui); font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-soft); margin-right: 0.2rem; }
.tk-chip-person { display: inline-flex; align-items: center; gap: 0.3rem; font-family: var(--font-ui); font-size: 0.75rem; color: var(--text); border: 1px solid var(--border); border-radius: 999px; padding: 0.12rem 0.45rem 0.12rem 0.2rem; background: var(--card); }
.tk-chip-person button { background: none; border: none; cursor: pointer; color: var(--text-soft); font-size: 0.85rem; padding: 0 0.1rem; line-height: 1; }
.tk-chip-person button:hover { color: var(--danger, #E06C5A); }
.tk-person-add { border: 1px dashed var(--border-strong, var(--border)); border-radius: 999px; background: transparent; color: var(--text); font-family: var(--font-ui); font-size: 0.75rem; padding: 0.18rem 0.6rem; min-width: 120px; }
.tk-person-add:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

/* backlog: acknowledged, not committed — deliberately the grey lane */
.g-backlog { background: var(--data-grey); color: var(--on-data-grey); }
.tk-status.s-backlog { color: var(--text-soft); border-style: dashed; }

/* every row's status wears its colour, like the group headers do */
.tk-status { font-weight: 600; }
.tk-status.s-todo { color: var(--data-amber-ink); border-color: var(--data-amber); }
.tk-status.s-in_progress { color: var(--data-blue-ink); border-color: var(--data-blue); }
.tk-status.s-blocked { color: var(--data-red-ink); border-color: var(--data-red); }
.tk-status.s-done { color: var(--data-green-ink); border-color: var(--data-green); }

/* tags: what kind of work it is, riding along whatever the status does */
.tk-tag { display: inline-block; margin-left: 0.4rem; font-family: var(--font-ui); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.03em; color: var(--text-soft); border: 1px solid var(--border); border-radius: 999px; padding: 0.05rem 0.45rem; background: var(--bg-soft); vertical-align: 1px; }
.tk-chip-tag { padding-left: 0.5rem; background: var(--bg-soft); }
.team-switch .chip.team-add { border-style: dashed; color: var(--text-soft); }
.team-switch .chip.team-add:hover { color: var(--text); border-color: var(--text-soft); }

/* reminders: a moment somebody asked to be told about */
.tk-remind-mark { display: inline-flex; align-items: center; gap: 0.2rem; margin-left: 0.4rem; font-family: var(--font-ui); font-size: 0.64rem; font-weight: 600; color: var(--accent); background: var(--accent-wash, var(--bg-soft)); border-radius: 999px; padding: 0.05rem 0.45rem; vertical-align: 1px; }
.tk-remind-preset { font-family: var(--font-ui); font-size: 0.72rem; color: var(--text-soft); background: transparent; border: 1px dashed var(--border-strong, var(--border)); border-radius: 999px; padding: 0.16rem 0.55rem; cursor: pointer; }
.tk-remind-preset:hover { color: var(--text); border-color: var(--text-soft); background: var(--bg-soft); }
.tk-chip-remind { background: var(--accent-wash, var(--bg-soft)); border-color: var(--accent); color: var(--accent); }
.tk-remind-at { min-width: 190px; }
.tk-scope-note { font-family: var(--font-ui); font-size: 0.7rem; color: var(--text-soft); background: var(--bg-soft); border: 1px solid var(--border); border-radius: 999px; padding: 0.15rem 0.6rem; }
.tk-alerts-btn { cursor: pointer; color: var(--accent); border-color: var(--accent); background: var(--accent-wash, var(--bg-soft)); }
.tk-alerts-btn:hover { background: var(--accent-tint, var(--bg-soft)); }
.tk-alerts-blocked { cursor: pointer; color: var(--text-soft); border-style: dashed; }

/* ---------------- board view: columns and cards ---------------- */
/* A horizontally scrolling row of columns; each column scrolls its own cards
   vertically. The board itself owns the horizontal scrollbar so the page
   underneath it never gains one.
   `align-items: stretch` (the flex default — kept explicit so nobody
   "cleans it up") makes every `.tk-col` the same height: the board's own
   height, not the tallest sibling's content. `flex-start` used to let each
   column size to its own content instead, so a column with zero or one card
   collapsed to a stub a few px tall — no room below the header for
   `.tk-col-cards` to occupy, so an empty Backlog/Done was there to look at
   but not to drop onto. Stretching means a column's height never depends on
   what its neighbours contain, which is exactly what makes an empty column a
   consistent drop target instead of a special case. */
.tk-board { display: flex; align-items: stretch; gap: 0.75rem; height: 100%; overflow-x: auto; overflow-y: hidden; padding-bottom: 0.5rem; }
.tk-col { flex: 0 0 280px; display: flex; flex-direction: column; max-height: 100%; background: var(--bg-soft); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 0.6rem; }
.tk-col header { display: flex; align-items: center; justify-content: space-between; gap: 0.4rem; margin-bottom: 0.55rem; padding: 0 0.15rem; }
.tk-col header h3 { margin: 0; font-family: var(--font-ui); font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-soft); }
.tk-col header b { font-family: var(--font-ui); font-size: 0.72rem; font-weight: 600; color: var(--text-faint, var(--text-soft)); }
/* `min-height: 5rem` is a floor, not the primary fix — the stretched column
   (above) is normally what gives an empty `.tk-col-cards` its real size.
   The floor only matters when the board itself has little height to give
   (a short viewport), so a drop target is never smaller than about one
   card's worth of room, no matter how the column ends up sized. */
.tk-col-cards { display: flex; flex-direction: column; gap: 0.5rem; flex: 1 1 auto; min-height: 5rem; overflow-y: auto; padding: 0.1rem; }
/* An empty column reuses the exact drop-slot look (`.tk-card-slot`, below) so
   it reads as "a card belongs here" at rest, not just while one is being
   dragged — the same dashed border, corner radius and tint, on the same
   token set. `::before` supplies the label without adding a DOM node, and
   generated content doesn't count against `:empty`, so this only ever shows
   when the column truly has no cards. */
.tk-col-cards:empty { align-items: center; justify-content: center; border: 2px dashed var(--border-hover); border-radius: var(--r-md); background: var(--accent-tint); }
.tk-col-cards:empty::before { content: "Drop a card here"; font-family: var(--font-ui); font-size: 0.72rem; color: var(--text-soft); }
.tk-more { align-self: flex-start; margin-top: 0.5rem; font-family: var(--font-ui); font-size: 0.72rem; color: var(--accent); background: none; border: none; cursor: pointer; padding: 0.2rem 0.15rem; }
.tk-more:hover { text-decoration: underline; }

/* `touch-action` leaves BOTH pans to the browser: cards fill their column, so
   `none` here meant a finger on the board could neither scroll a column nor
   swipe between them — the board was barely usable on a phone. A touch drag is
   gated on a long press instead (tracker.js's DRAG_HOLD_MS), which is the
   pattern every other board uses and the only one that can share the surface
   with scrolling. `pan-y` alone would still have blocked the column-to-column
   swipe the mobile layout's scroll-snap is built on, so both axes stay. */
.tk-card { display: flex; flex-direction: column; gap: 0.45rem; background: var(--card); border: 1px solid var(--border); border-radius: var(--r-md); padding: 0.55rem 0.6rem; cursor: grab; touch-action: pan-x pan-y; }
.tk-card:hover { border-color: var(--border-hover); }
.tk-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tk-card-title { text-align: left; background: none; border: none; cursor: pointer; padding: 0; font-family: var(--font-ui); font-size: 0.82rem; line-height: 1.35; color: var(--text); }
.tk-card-meta, .tk-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.4rem; }
.tk-card .tk-status, .tk-card .tk-priority, .tk-card .tk-date { font-size: 0.68rem; padding: 0.16rem 0.3rem; }
.tk-card .tk-people-cell { padding: 0; font-size: 0.72rem; }
.tk-source-mark { display: inline-flex; vertical-align: -2px; margin-left: 0.3rem; color: var(--text-faint, var(--text-soft)); }
.tk-source-mark svg { width: 11px; height: 11px; display: block; }

/* Dragging a card: a fixed-position clone follows the pointer, a dashed slot
   marks where it would land, and the original is taken out of the column's
   flow without losing its space in the DOM (so the drag can still find it). */
/* The long press landed and the card is now the finger's to move — said
   before it has moved at all, so a held card never looks like an ignored one.
   Suppressed for anyone who asked for less motion; the shadow still reads. */
.tk-card-held { box-shadow: var(--shadow-2); cursor: grabbing; transform: scale(1.02); transition: transform 0.12s ease, box-shadow 0.12s ease; }
@media (prefers-reduced-motion: reduce) { .tk-card-held { transform: none; transition: none; } }
.tk-card-hiding { display: none; }
.tk-card-slot { border: 2px dashed var(--border-hover); border-radius: var(--r-md); background: var(--accent-tint); }
.tk-card-ghost { position: fixed; z-index: 80; cursor: grabbing; box-shadow: var(--shadow-2); opacity: 0.94; transform: rotate(1deg); pointer-events: none; margin: 0 !important; }

@media (max-width: 768px) {
  /* One column at a time, snapped edge to edge — the reason this is pointer
     events and not a hover-dependent interaction in the first place. */
  .tk-board { scroll-snap-type: x mandatory; }
  .tk-col { flex-basis: calc(100vw - 2rem); scroll-snap-align: start; }
}
