
    *{margin:0;padding:0;box-sizing:border-box}
    body{font-family:var(--ss-font-sans);background:var(--ss-bg);color:var(--ss-text);height:100vh;height:100dvh;display:flex;flex-direction:column}
    /* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/dmsans/v17/rP2Yp2ywxg089UriI5-g4vlH9VoD8Cmcqbu6-K6z9mXgjU0.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;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/dmsans/v17/rP2Yp2ywxg089UriI5-g4vlH9VoD8Cmcqbu0-K6z9mXg.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;
}

:root {
  --ss-font-sans: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --ss-font-logo: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
body { font-family: var(--ss-font-sans); }
button, input, select, textarea { font-family: inherit; }
:root {
  /* A caption laid over a page's picture (Paddock tiles, space-card.ts) — not --ss-scrim,
     the modal backdrop. Paper in the light theme, near-black in the dark one, carrying the
     page's own inks; -mid is its opacity where the title starts, -end at the foot. Sized
     so --ss-text-subtle stays AA over the darkest page in light (95%: 4.7:1) and the
     lightest in dark (85%: 5.2:1). */
  --ss-caption-scrim: #ffffff;
  --ss-caption-scrim-mid: 95%;
  --ss-caption-scrim-end: 98%;
  color-scheme: light;

  /* Surfaces, lightest page ground to the cards that sit on it. */
  --ss-bg: #f8f7f4;
  --ss-bg-chrome: #f8f7f4;
  --ss-surface: #fff;
  --ss-surface-muted: #f4f2ee;
  --ss-surface-soft: #eeecea;
  --ss-surface-soft-hover: #e0ddd8;
  --ss-surface-raised: #fff;

  /* Text, by role. muted/subtle/faint descend in weight, never below AA — which
     the faint one did not honour: at #99968f it was 2.8:1 on the page ground and
     2.9:1 on a card, and it carries the hints, the footer and the account email.
     Ratios now: 15.9 / 9.8 / 5.3 / 4.6 on a card. */
  --ss-text: #1a1a1f;
  --ss-text-muted: #3a3a42;
  --ss-text-subtle: #6b6b78;
  --ss-text-faint: #74716a;
  --ss-text-placeholder: #9e9b94;

  /* A deliberately inverted card (the coach tip, the org chip, a heatmap tooltip).
     Its own text scale, because the page's greys are lit for a light ground and
     would disappear on it. */
  --ss-inverse-surface: #1a1a1f;
  --ss-inverse-surface-hover: #26262e;
  --ss-inverse-text: #fff;
  --ss-inverse-text-subtle: #bdbcc6;

  /* What a text field is filled with — the white the browser used to supply. */
  --ss-field: #fff;

  --ss-border: #e0ddd8;
  --ss-border-strong: #d0cdc8;
  --ss-border-soft: #eeecea;

  --ss-toggle-on-bg: #5950c9;
  --ss-toggle-on-text: #fff;
  --ss-toggle-off-bg: #a34d48;
  --ss-toggle-off-text: #fff;
  --ss-brand: #5a5ae6;
  --ss-brand-hover: #4a4ad6;
  --ss-brand-text: #5a5ae6;
  --ss-brand-text-hover: #4a4ad6;
  --ss-brand-deep: #5750b5;
  --ss-focus: #5a5ae6;
  --ss-brand-tint: #eeeaf8;
  --ss-brand-tint-border: #cdc9f2;
  --ss-on-brand: #fff;
  --ss-focus-ring: rgba(90, 90, 230, 0.1);

  --ss-danger: #cc4444;
  --ss-danger-text: #b4453a;
  --ss-danger-tint: #fbf0ef;
  --ss-danger-tint-border: #e6c4c0;

  --ss-success: #2d8f5e;
  --ss-success-text: #1f7a4d;
  --ss-success-tint: #f0faf4;
  --ss-success-tint-border: #bfe6cf;

  --ss-attention: #f77000;

  --ss-warn: #b4761f;
  --ss-warn-text: #a86f10;
  --ss-warn-tint: #fdf3d8;
  --ss-warn-tint-border: #e6e0d2;

  --ss-tile-indigo: #ecebff;
  --ss-tile-indigo-ink: #5a5ae6;
  --ss-tile-amber: #fff0dc;
  --ss-tile-amber-ink: #e08a34;
  --ss-tile-pink: #ffe7f0;
  --ss-tile-pink-ink: #e0568a;
  --ss-tile-teal: #e2f6f4;
  --ss-tile-teal-ink: #00756e;

  /* The crown is the admin marking (see CLAUDE.md) — one gold, both themes. */
  --ss-crown: #c9a227;
  /* Art plates are lit for a light page, so here they are shown as drawn. */
  --ss-art-dim: none;

  /* A dark object on a light page: the terminal demo and the install pill. */
  --ss-deep: #16161c;
  --ss-deep-soft: #2b2b35;
  --ss-deep-border: #26262e;
  --ss-deep-text: #e8e8ea;
  --ss-deep-text-subtle: #7a7a86;

  /* Elevation as three steps, one token each, so a card asks for a step by name
     and gets the cue its theme needs. Here that is an ordinary cast shadow; the
     dark theme redefines the same step as a lit top edge, because a black shadow
     on a black ground says nothing. A shadow literal at the call site can only
     ever be right in one theme — that is the bug these replace. */
  --ss-elev-1: 0 1px 2px rgba(20, 20, 30, 0.05), 0 1px 3px rgba(20, 20, 30, 0.05);
  --ss-elev-2: 0 2px 4px rgba(20, 20, 30, 0.05), 0 10px 22px -12px rgba(20, 20, 30, 0.3);
  --ss-elev-pop: 0 6px 20px rgba(20, 20, 30, 0.14), 0 1px 3px rgba(20, 20, 30, 0.08);

  --ss-shadow: rgba(0, 0, 0, 0.12);
  --ss-shadow-soft: rgba(0, 0, 0, 0.06);
  --ss-scrim: rgba(0, 0, 0, 0.45);
  /* The scrim darkens the page it covers (a modal behind its backdrop). The veil
     REPLACES it — the loading overlay is the page ground itself, near-opaque, with
     the sheep on top — so it follows the theme instead of always darkening. */
  --ss-veil: rgba(248, 247, 244, 0.9);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
color-scheme: dark;

  /* The neutrals are a cool slate, not a warm near-black. Two reasons. A dashboard
     is mostly greys, so whatever hue they carry IS the page's colour — and an
     unhued near-black reads as absence rather than as a choice. And the ground sits
     high enough to be a room with a lamp in it: a card lifted off #1c2028 is a
     surface, where the same card over near-black was a rectangle floating in a void.
     Every step below is ~4% white over the one above it, which is the same interval
     the surfaces keep in the light theme. */
  /* The bar the header sits on is DEEPER than the content ground, not equal to
     it. On a lifted ground a header painted in the page colour has no edge and
     the page starts nowhere; dropping it below the content is what makes the
     chrome read as a frame around the work. In the light theme there is nothing
     below the page ground to drop to, so it stays the page colour. */
  --ss-bg-chrome: #14171d;

  --ss-bg: #1c2028;
  --ss-surface: #252a34;
  --ss-surface-muted: #2c313d;
  --ss-surface-soft: #343a47;
  --ss-surface-soft-hover: #3f4654;
  --ss-surface-raised: #282e39;

  /* Text is near-white and cool, so it belongs to the same slate as the surfaces
     rather than sitting on them as a separate warm grey. Ratios on the card:
     13.1 / 8.9 / 6.2 / 5.5 — every role clears AA on the page ground and a card. */
  --ss-text: #f2f4f8;
  --ss-text-muted: #c6ccd8;
  --ss-text-subtle: #a2aab8;
  --ss-text-faint: #98a1b0;
  /* Hint text inside an empty field: lighter than any text a person wrote, so an
     example never reads as something already filled in. Not a reading role. */
  --ss-text-placeholder: #6b7383;

  /* An inverted fill (the org chip, a solid dark button) can't stay near-black on a
     dark page — it would vanish into it — so it lifts to a raised grey instead, and
     the white text on it is white in both themes. */
  --ss-inverse-surface: #3b4353;
  --ss-inverse-surface-hover: #46505f;
  --ss-inverse-text: #fff;
  --ss-inverse-text-subtle: #bcc3d0;

  /* Hairlines. The plain and soft ones are decoration — a drawn edge rather than
     a second fill, which is what keeps a page of cards from reading as a grid of
     boxes, and they stay faint on purpose. The STRONG one is not decoration: it is
     the edge that says "this is a control" on an input, a select, a ghost button,
     and WCAG 1.4.11 asks 3:1 of it against every surface it can sit on. At the old
     #4a5262 that was 1.8:1 and a text field was floating text on a dark card. This
     clears 3:1 on the ground, a card and a soft fill alike. */
  --ss-border: #363d4b;
  --ss-border-strong: #79849a;
  --ss-border-soft: #2a303b;

  /* The fill inside a text field. A field that sets no background of its own gets
     the browser's, and the UA's dark fill is a warm #3b3b3b that sits wrong on
     slate — the one colour on the page nobody chose. */
  --ss-field: #343a47;

  /* Two brands, because the job splits in the dark: a FILL has white text on it and
     so must stay dark enough for that (5.8:1), while brand TEXT sits on the page
     ground and must be light enough to read there (7.1:1). One value can't do both.
     The violet is deliberately more saturated than the light theme's indigo: the
     same chroma that is plenty on paper goes grey against a dark ground, and this
     is the one colour on a page of slate. */
  --ss-toggle-on-bg: #aaa3ff;
  --ss-toggle-on-text: #201b49;
  --ss-toggle-off-bg: #e4a29c;
  --ss-toggle-off-text: #411c1a;
  --ss-brand: #6c3cf5;
  --ss-brand-hover: #7d52ff;
  --ss-brand-text: #b39dff;
  --ss-brand-text-hover: #c7b6ff;
  --ss-brand-deep: #c5b5ff;
  --ss-brand-tint: #2b2352;
  --ss-brand-tint-border: #453573;
  --ss-on-brand: #fff;
  /* The keyboard focus ring. It is its own token because the brand FILL colour,
     which every ring used to be drawn in, is 2.8:1 on this ground — under the 3:1
     that 1.4.11 asks of a focus indicator, and the one marking a keyboard user
     navigates by. Brand TEXT is already lit for this ground, so the ring borrows
     it. In the light theme the two are the same value and nothing changes. */
  --ss-focus: #b39dff;
  /* The halo a focused field wears. It is a token because the same alpha that
     reads as a soft ring on a white field all but vanishes on a dark one. */
  --ss-focus-ring: rgba(140, 106, 255, 0.28);

  --ss-danger: #ff8080;
  --ss-danger-text: #ff9a90;
  --ss-danger-tint: #42292e;
  --ss-danger-tint-border: #6b3f47;

  /* Mint rather than the light theme's forest green: at this lightness a green with
     any yellow in it goes olive, and the cool version sits with the slate. */
  --ss-success: #34d399;
  --ss-success-text: #6ee7b7;
  --ss-success-tint: #16352c;
  --ss-success-tint-border: #256552;

  --ss-attention: #ff963f;

  --ss-warn: #e0a94a;
  --ss-warn-text: #ecbc63;
  --ss-warn-tint: #3d341b;
  --ss-warn-tint-border: #635632;

  /* Decorative accent tiles (the Flock reel's three glyphs). A tint keeps its hue
     across themes; only its lightness flips. */
  --ss-tile-indigo: #2b2352;
  --ss-tile-indigo-ink: #b39dff;
  --ss-tile-amber: #41361a;
  --ss-tile-amber-ink: #eab26a;
  --ss-tile-pink: #412938;
  --ss-tile-pink-ink: #f58fb1;
  --ss-tile-teal: #123a38;
  --ss-tile-teal-ink: #5fd4c9;

  --ss-crown: #d9b43d;

  /* An object that is dark in BOTH themes — the landing page's terminal, the
     install pill. On paper it is the one dark thing; on slate it has to sit below
     the ground, the way the reference site drops its code panels under the page,
     or it vanishes into it. Its text scale is its own, like the inverse card's. */
  --ss-deep: #0f1116;
  --ss-deep-soft: #252b36;
  --ss-deep-border: #2a3140;
  --ss-deep-text: #e8ecf3;
  --ss-deep-text-subtle: #7c8798;

  /* The starter illustrations are line art drawn on cream paper, and they stay
     that way in the dark — an inverted plate would wreck the drawing, which is the
     same reason a published space is never inverted. But at full brightness three
     of them side by side are a lit band across a dark page and the eye goes
     nowhere else, so the lamp is turned down rather than the paper repainted. */
  --ss-art-dim: brightness(0.84) saturate(0.94);

  /* Depth, in the dark, is not a shadow — a black shadow cast on a dark ground is
     invisible, which is why every card here used to read as an outline rather than
     a sheet. It is a lit top edge instead: the inset is the light the room would
     catch on the card's near corner, and the shadow underneath only seats it. See
     the light theme's note for why this is a token. */
  --ss-elev-1: inset 0 1px 0 rgba(255, 255, 255, 0.055), 0 1px 2px rgba(0, 0, 0, 0.45);
  --ss-elev-2: inset 0 1px 0 rgba(255, 255, 255, 0.09), 0 10px 26px -12px rgba(0, 0, 0, 0.75), 0 2px 6px rgba(0, 0, 0, 0.4);
  --ss-elev-pop: inset 0 1px 0 rgba(255, 255, 255, 0.07), 0 12px 32px rgba(0, 0, 0, 0.6);

  --ss-shadow: rgba(0, 0, 0, 0.5);
  --ss-shadow-soft: rgba(0, 0, 0, 0.35);
  --ss-scrim: rgba(0, 0, 0, 0.6);
  --ss-caption-scrim: #121218;
  --ss-caption-scrim-mid: 85%;
  --ss-caption-scrim-end: 92%;
  --ss-veil: rgba(28, 32, 40, 0.92);
  }
}

:root[data-theme="dark"] {
color-scheme: dark;

  /* The neutrals are a cool slate, not a warm near-black. Two reasons. A dashboard
     is mostly greys, so whatever hue they carry IS the page's colour — and an
     unhued near-black reads as absence rather than as a choice. And the ground sits
     high enough to be a room with a lamp in it: a card lifted off #1c2028 is a
     surface, where the same card over near-black was a rectangle floating in a void.
     Every step below is ~4% white over the one above it, which is the same interval
     the surfaces keep in the light theme. */
  /* The bar the header sits on is DEEPER than the content ground, not equal to
     it. On a lifted ground a header painted in the page colour has no edge and
     the page starts nowhere; dropping it below the content is what makes the
     chrome read as a frame around the work. In the light theme there is nothing
     below the page ground to drop to, so it stays the page colour. */
  --ss-bg-chrome: #14171d;

  --ss-bg: #1c2028;
  --ss-surface: #252a34;
  --ss-surface-muted: #2c313d;
  --ss-surface-soft: #343a47;
  --ss-surface-soft-hover: #3f4654;
  --ss-surface-raised: #282e39;

  /* Text is near-white and cool, so it belongs to the same slate as the surfaces
     rather than sitting on them as a separate warm grey. Ratios on the card:
     13.1 / 8.9 / 6.2 / 5.5 — every role clears AA on the page ground and a card. */
  --ss-text: #f2f4f8;
  --ss-text-muted: #c6ccd8;
  --ss-text-subtle: #a2aab8;
  --ss-text-faint: #98a1b0;
  /* Hint text inside an empty field: lighter than any text a person wrote, so an
     example never reads as something already filled in. Not a reading role. */
  --ss-text-placeholder: #6b7383;

  /* An inverted fill (the org chip, a solid dark button) can't stay near-black on a
     dark page — it would vanish into it — so it lifts to a raised grey instead, and
     the white text on it is white in both themes. */
  --ss-inverse-surface: #3b4353;
  --ss-inverse-surface-hover: #46505f;
  --ss-inverse-text: #fff;
  --ss-inverse-text-subtle: #bcc3d0;

  /* Hairlines. The plain and soft ones are decoration — a drawn edge rather than
     a second fill, which is what keeps a page of cards from reading as a grid of
     boxes, and they stay faint on purpose. The STRONG one is not decoration: it is
     the edge that says "this is a control" on an input, a select, a ghost button,
     and WCAG 1.4.11 asks 3:1 of it against every surface it can sit on. At the old
     #4a5262 that was 1.8:1 and a text field was floating text on a dark card. This
     clears 3:1 on the ground, a card and a soft fill alike. */
  --ss-border: #363d4b;
  --ss-border-strong: #79849a;
  --ss-border-soft: #2a303b;

  /* The fill inside a text field. A field that sets no background of its own gets
     the browser's, and the UA's dark fill is a warm #3b3b3b that sits wrong on
     slate — the one colour on the page nobody chose. */
  --ss-field: #343a47;

  /* Two brands, because the job splits in the dark: a FILL has white text on it and
     so must stay dark enough for that (5.8:1), while brand TEXT sits on the page
     ground and must be light enough to read there (7.1:1). One value can't do both.
     The violet is deliberately more saturated than the light theme's indigo: the
     same chroma that is plenty on paper goes grey against a dark ground, and this
     is the one colour on a page of slate. */
  --ss-toggle-on-bg: #aaa3ff;
  --ss-toggle-on-text: #201b49;
  --ss-toggle-off-bg: #e4a29c;
  --ss-toggle-off-text: #411c1a;
  --ss-brand: #6c3cf5;
  --ss-brand-hover: #7d52ff;
  --ss-brand-text: #b39dff;
  --ss-brand-text-hover: #c7b6ff;
  --ss-brand-deep: #c5b5ff;
  --ss-brand-tint: #2b2352;
  --ss-brand-tint-border: #453573;
  --ss-on-brand: #fff;
  /* The keyboard focus ring. It is its own token because the brand FILL colour,
     which every ring used to be drawn in, is 2.8:1 on this ground — under the 3:1
     that 1.4.11 asks of a focus indicator, and the one marking a keyboard user
     navigates by. Brand TEXT is already lit for this ground, so the ring borrows
     it. In the light theme the two are the same value and nothing changes. */
  --ss-focus: #b39dff;
  /* The halo a focused field wears. It is a token because the same alpha that
     reads as a soft ring on a white field all but vanishes on a dark one. */
  --ss-focus-ring: rgba(140, 106, 255, 0.28);

  --ss-danger: #ff8080;
  --ss-danger-text: #ff9a90;
  --ss-danger-tint: #42292e;
  --ss-danger-tint-border: #6b3f47;

  /* Mint rather than the light theme's forest green: at this lightness a green with
     any yellow in it goes olive, and the cool version sits with the slate. */
  --ss-success: #34d399;
  --ss-success-text: #6ee7b7;
  --ss-success-tint: #16352c;
  --ss-success-tint-border: #256552;

  --ss-attention: #ff963f;

  --ss-warn: #e0a94a;
  --ss-warn-text: #ecbc63;
  --ss-warn-tint: #3d341b;
  --ss-warn-tint-border: #635632;

  /* Decorative accent tiles (the Flock reel's three glyphs). A tint keeps its hue
     across themes; only its lightness flips. */
  --ss-tile-indigo: #2b2352;
  --ss-tile-indigo-ink: #b39dff;
  --ss-tile-amber: #41361a;
  --ss-tile-amber-ink: #eab26a;
  --ss-tile-pink: #412938;
  --ss-tile-pink-ink: #f58fb1;
  --ss-tile-teal: #123a38;
  --ss-tile-teal-ink: #5fd4c9;

  --ss-crown: #d9b43d;

  /* An object that is dark in BOTH themes — the landing page's terminal, the
     install pill. On paper it is the one dark thing; on slate it has to sit below
     the ground, the way the reference site drops its code panels under the page,
     or it vanishes into it. Its text scale is its own, like the inverse card's. */
  --ss-deep: #0f1116;
  --ss-deep-soft: #252b36;
  --ss-deep-border: #2a3140;
  --ss-deep-text: #e8ecf3;
  --ss-deep-text-subtle: #7c8798;

  /* The starter illustrations are line art drawn on cream paper, and they stay
     that way in the dark — an inverted plate would wreck the drawing, which is the
     same reason a published space is never inverted. But at full brightness three
     of them side by side are a lit band across a dark page and the eye goes
     nowhere else, so the lamp is turned down rather than the paper repainted. */
  --ss-art-dim: brightness(0.84) saturate(0.94);

  /* Depth, in the dark, is not a shadow — a black shadow cast on a dark ground is
     invisible, which is why every card here used to read as an outline rather than
     a sheet. It is a lit top edge instead: the inset is the light the room would
     catch on the card's near corner, and the shadow underneath only seats it. See
     the light theme's note for why this is a token. */
  --ss-elev-1: inset 0 1px 0 rgba(255, 255, 255, 0.055), 0 1px 2px rgba(0, 0, 0, 0.45);
  --ss-elev-2: inset 0 1px 0 rgba(255, 255, 255, 0.09), 0 10px 26px -12px rgba(0, 0, 0, 0.75), 0 2px 6px rgba(0, 0, 0, 0.4);
  --ss-elev-pop: inset 0 1px 0 rgba(255, 255, 255, 0.07), 0 12px 32px rgba(0, 0, 0, 0.6);

  --ss-shadow: rgba(0, 0, 0, 0.5);
  --ss-shadow-soft: rgba(0, 0, 0, 0.35);
  --ss-scrim: rgba(0, 0, 0, 0.6);
  --ss-caption-scrim: #121218;
  --ss-caption-scrim-mid: 85%;
  --ss-caption-scrim-end: 92%;
  --ss-veil: rgba(28, 32, 40, 0.92);
}

/* The floor under every text field, so an input that styles only its border still
   sits on a colour from this palette rather than on the browser's own. Zero
   specificity on purpose (:where): anything that sets its own background or
   placeholder wins, and the checkbox-shaped inputs are left to the UA, which draws
   them from color-scheme. Without the :where, the six :not() clauses outweighed a
   plain ".card input { background: transparent }" and painted a lighter box
   inside every borderless field in the dark. */
:where(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]),
select,
textarea) {
  background: var(--ss-field);
  color: var(--ss-text);
}
input::placeholder, textarea::placeholder { color: var(--ss-text-subtle); opacity: 1; }
.speak {
  position: fixed; z-index: 39; display: flex; align-items: center;
  right: max(16px, env(safe-area-inset-right)); bottom: calc(16px + env(safe-area-inset-bottom, 0px));
}
.speak-btn {
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  width: 52px; height: 52px; padding: 0; border-radius: 50%; font: inherit; line-height: 1;
  color: var(--ss-text); background: var(--ss-surface-raised);
  border: 1px solid var(--ss-border-strong); box-shadow: var(--ss-elev-pop);
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.speak-btn { touch-action: none; }
.speak-btn svg { width: 20px; height: 20px; }
.speak.is-dragging .speak-btn { cursor: grabbing; transition: none; }
.speak-btn:hover { background: var(--ss-surface-soft); }
.speak-btn:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: 2px; }
.speak.is-live .speak-btn { color: var(--ss-danger-text); border-color: var(--ss-danger-tint-border); background: var(--ss-danger-tint); }
.speak.is-busy .speak-btn { color: var(--ss-brand-text); border-color: var(--ss-brand-tint-border); background: var(--ss-brand-tint); }
.speak-pop {
  position: fixed; z-index: 55;
  right: max(16px, env(safe-area-inset-right)); bottom: calc(80px + env(safe-area-inset-bottom, 0px));
  width: min(420px, calc(100vw - 2rem)); padding: 0.9rem 1rem 0.8rem;
  background: var(--ss-surface-raised); color: var(--ss-text);
  border: 1px solid var(--ss-border); border-radius: 12px; box-shadow: var(--ss-elev-pop);
}
.speak-pop[hidden] { display: none; }
.speak-state { display: flex; align-items: center; gap: 0.45rem; font-size: 0.75rem; color: var(--ss-text-subtle); }
.speak-dot { width: 8px; height: 8px; border-radius: 999px; background: var(--ss-text-faint); flex: none; }
.speak.is-live .speak-dot { background: var(--ss-danger); animation: speak-pulse 1.2s ease-in-out infinite; }
.speak.is-busy .speak-dot, .speak.is-running .speak-dot { background: var(--ss-brand); animation: speak-pulse 0.8s ease-in-out infinite; }
@keyframes speak-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.speak-time { margin-left: auto; font-variant-numeric: tabular-nums; }
/* Close, top right beside the clock: the same corner × as the working card's
   (.ss-work-x in packages/app/src/pages/viewer.ts). Negative margins keep a 28px
   target from growing the state row. */
.speak-x {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; margin: -6px -6px -6px 0; padding: 0;
  border: 0; border-radius: 8px; background: transparent; color: var(--ss-text-faint); cursor: pointer;
}
.speak-x:hover { background: var(--ss-surface-soft); color: var(--ss-text); }
.speak-x:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: 1px; }
.speak-text {
  min-height: 2.6rem; max-height: 10rem; overflow-y: auto; margin: 0.6rem 0 0.7rem;
  font-size: 0.95rem; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
}
.speak-text .speak-interim { color: var(--ss-text-faint); }
.speak-text .speak-hint { color: var(--ss-text-placeholder); }
/* The guesses: spaces whose titles match the words heard so far, shown before the
   model answers. Rows look like the header search's type-ahead rows, because they are
   the same thing. Once the model answers they are marked (the one it opened) or dimmed
   (it did something else) — never moved. */
.speak-guess { margin: -0.3rem 0 0.75rem; }
.speak-guess[hidden] { display: none; }
.speak-guess-h { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; margin-bottom: 0.15rem; font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ss-text-faint); }
.speak-guess-k { text-transform: none; letter-spacing: 0; font-weight: 500; }
.speak-guess-o {
  display: flex; align-items: center; gap: 0.6rem; min-width: 0; margin: 0 -0.5rem; padding: 0.4rem 0.5rem;
  border-radius: 8px; color: var(--ss-text); text-decoration: none; animation: speak-guess-in 0.16s ease-out;
}
.speak-guess-o:hover { background: var(--ss-surface-soft); }
.speak-guess-o:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: -2px; }
.speak-guess-o.is-chosen { background: var(--ss-brand-tint); }
.speak-guess-e { flex: none; width: 1.4rem; text-align: center; font-size: 1rem; line-height: 1; }
.speak-guess-b { display: flex; flex-direction: column; flex: 1; min-width: 0; gap: 0.1rem; }
.speak-guess-t, .speak-guess-s { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.speak-guess-t { font-size: 0.88rem; }
.speak-guess-t mark { background: transparent; color: inherit; font-weight: 700; }
.speak-guess-s { font-size: 0.74rem; color: var(--ss-text-faint); }
.speak-guess-go { flex: none; font-size: 0.76rem; font-weight: 600; color: var(--ss-brand-text); }
.speak-guess.is-passed .speak-guess-o:not(:hover):not(:focus-visible) { opacity: 0.55; }
/* Speak's cache: a row their own past picks put there, and the line on the page the
   cache opened while the model still checks it. */
.speak-guess-c {
  display: inline-block; margin-right: 0.35rem; padding: 0 0.4rem; border-radius: 999px;
  font-size: 0.66rem; font-weight: 600; line-height: 1.15rem; letter-spacing: 0.02em; vertical-align: 1px;
  color: var(--ss-brand-text); background: var(--ss-brand-tint); border: 1px solid var(--ss-brand-tint-border);
}
.speak-cache { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 0.1rem; margin-bottom: 0.45rem; color: var(--ss-text); }
.speak-cache-t { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: calc(100% - 6rem); }
.speak-cache-s { flex-basis: 100%; display: flex; align-items: flex-start; gap: 0.45rem; font-size: 0.8rem; color: var(--ss-text-subtle); }
.speak-cache-s .speak-spin { margin-top: 0.15rem; }
@keyframes speak-guess-in { from { opacity: 0; transform: translateY(2px); } }
.speak-out { margin: -0.2rem 0 0.75rem; padding-top: 0.6rem; border-top: 1px solid var(--ss-border-soft); font-size: 0.88rem; line-height: 1.45; color: var(--ss-text-muted); }
.speak-out[hidden] { display: none; }
.speak-say { margin: 0 0 0.35rem; color: var(--ss-text); }
.speak-say:last-child { margin-bottom: 0; }
/* What a model wrote (a reply, a Co-shepherd run's ending) is Markdown: rendered
   by the chat renderer (assets/features/chat-markdown.ts), so a link is a link. */
.speak-md { overflow-wrap: anywhere; }
.speak-md p, .speak-md ul, .speak-md ol { margin: 0; }
.speak-md p + p, .speak-md p + ul, .speak-md p + ol, .speak-md ul + p, .speak-md ol + p, .speak-md .md-h + p { margin-top: 0.4rem; }
.speak-md ul, .speak-md ol { padding-left: 1.2rem; }
.speak-md li + li { margin-top: 0.15rem; }
.speak-md .md-h { font-weight: 600; }
.speak-md a { color: var(--ss-brand-text); text-decoration: underline; text-underline-offset: 2px; }
.speak-md code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.86em; }
.speak-md pre { overflow-x: auto; margin: 0.4rem 0 0; }
.speak-list-title { font-size: 0.75rem; color: var(--ss-text-subtle); margin-bottom: 0.3rem; }
.speak-list { margin: 0 0 0.2rem 1.3rem; padding: 0; }
.speak-list li { margin: 0.15rem 0; }
.speak-list a { color: var(--ss-brand-text); text-decoration: none; }
.speak-list a:hover { text-decoration: underline; }
.speak-confirm { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; }
.speak-confirm span { margin-right: auto; color: var(--ss-text); }
.speak-act--danger { background: var(--ss-danger-tint); border-color: var(--ss-danger-tint-border); color: var(--ss-danger-text); }
.speak-foot { display: flex; align-items: center; gap: 0.5rem; }
/* A failed ask (variant C of design-explorations/speak-try-again.html): the heard
   words become a field to fix, with Speak again and Try again beside it. */
.speak-fix {
  display: flex; align-items: center; gap: 8px; margin: 0.6rem 0 0.7rem; padding: 0.45rem 0.6rem;
  border: 1px solid var(--ss-border-strong); border-radius: 8px; background: var(--ss-surface-muted); cursor: text;
}
.speak-fix[hidden] { display: none; }
.speak-fix:focus-within { border-color: var(--ss-focus); box-shadow: 0 0 0 2px var(--ss-brand-tint); }
.speak-fix input { flex: 1; min-width: 0; margin: 0; padding: 0; border: 0; background: none; outline: none; font: inherit; font-size: 0.95rem; line-height: 1.4; color: var(--ss-text); }
.speak-fix-ic { display: inline-flex; color: var(--ss-text-faint); }
.speak-fix-ic svg { width: 15px; height: 15px; }
.speak-act--icon { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.speak-act--icon svg { width: 14px; height: 14px; }
a.speak-act--send { text-decoration: none; }
.speak-act[hidden] { display: none; }
.speak-meta { font-size: 0.7rem; color: var(--ss-text-faint); margin-right: auto; font-variant-numeric: tabular-nums; }
.speak-meta a { color: var(--ss-brand-text); }
.speak-act {
  font: inherit; font-size: 0.8rem; font-weight: 500; cursor: pointer; border-radius: 8px;
  padding: 0.4rem 0.8rem; border: 1px solid var(--ss-border); background: var(--ss-surface); color: var(--ss-text-muted);
}
.speak-act:hover { color: var(--ss-text); border-color: var(--ss-border-strong); }
.speak-act--send { background: var(--ss-brand); border-color: var(--ss-brand); color: var(--ss-on-brand); }
.speak-act--send:hover { background: var(--ss-brand-hover); border-color: var(--ss-brand-hover); color: var(--ss-on-brand); }
.speak-act:disabled { opacity: 0.5; cursor: default; }
/* Talkback's switch: whether Speak says its replies aloud. Admins only, so it wears
   the crown badge on its corner (admin-badge.ts). */
.speak-voice { position: relative; display: inline-flex; align-items: center; padding: 0.4rem 0.55rem; }
.speak-voice[hidden] { display: none; }
.speak-voice-ic { display: inline-flex; }
.speak-voice-ic svg { width: 15px; height: 15px; }
/* On: the speaker. Muted: the same speaker with the slash (icon "sound-off"). */
.speak-voice[aria-pressed="true"] .speak-voice-ic--off, .speak-voice[aria-pressed="false"] .speak-voice-ic--on { display: none; }
.ss-admin-badge{position:absolute;top:-7px;right:-7px;width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center;border-radius:50%;background:var(--ss-surface-raised);border:1px solid var(--ss-border);box-shadow:var(--ss-elev-1);color:var(--ss-crown);pointer-events:none}
.ss-admin-badge svg{width:11px;height:11px}
/* A hand-up to the Co-shepherd: the card stays and follows the run until it ends,
   a hairline sweeping along its top edge, the step in hand, the steps behind it.
   Hidden, the run is a ring around the mic. */
.speak-bar { position: absolute; top: 0; left: 12px; right: 12px; height: 2px; overflow: hidden; border-radius: 2px; background: var(--ss-brand-tint); }
.speak-bar[hidden] { display: none; }
.speak-bar::after { content: ""; position: absolute; inset: 0; width: 35%; background: var(--ss-brand); animation: speak-sweep 1.6s ease-in-out infinite; }
@keyframes speak-sweep { from { transform: translateX(-100%); } to { transform: translateX(300%); } }
.speak-run-now { display: flex; align-items: flex-start; gap: 0.55rem; font-size: 0.9rem; font-weight: 600; color: var(--ss-text); line-height: 1.4; }
.speak-run-now.is-prose { font-weight: 400; font-style: italic; color: var(--ss-text-muted); }
.speak-run-sum { margin: 0; font-size: 0.92rem; color: var(--ss-text); line-height: 1.5; max-height: 12rem; overflow-y: auto; }
.speak-spin { flex: none; width: 12px; height: 12px; margin-top: 0.2rem; border-radius: 50%; border: 2px solid var(--ss-brand-tint-border); border-top-color: var(--ss-brand); animation: speak-rot 0.9s linear infinite; }
@keyframes speak-rot { to { transform: rotate(360deg); } }
.speak-run-trail { list-style: none; margin: 0.45rem 0 0; padding: 0; }
.speak-run-trail li { display: flex; align-items: baseline; gap: 0.55rem; padding: 0.12rem 0; font-size: 0.78rem; color: var(--ss-text-subtle); }
.speak-run-trail .speak-ck { flex: none; width: 12px; text-align: center; color: var(--ss-success-text); font-weight: 700; }
.speak-run-trail .speak-st { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.speak-run-trail .speak-dur { flex: none; font-variant-numeric: tabular-nums; color: var(--ss-text-faint); }
.speak-link { display: inline-block; margin-top: 0.55rem; padding: 0; border: 0; background: none; font: inherit; font-size: 0.8rem; font-weight: 600; color: var(--ss-brand-text); cursor: pointer; }
.speak-link:hover { color: var(--ss-brand-text-hover); text-decoration: underline; }
.speak.is-running .speak-btn { color: var(--ss-brand-text); border-color: var(--ss-brand-tint-border); box-shadow: 0 0 0 4px var(--ss-brand-tint), var(--ss-elev-pop); }
@media (prefers-reduced-motion: reduce) { .speak.is-live .speak-dot, .speak.is-running .speak-dot, .speak-bar::after, .speak-spin, .speak-guess-o { animation: none; } }
/* The floating Feedback pill (assets/shell/feedback-cta.ts: right 24, bottom 24, 34 tall,
   shown from 1024px) had the corner first: the mic stacks above it, right edges in line. */
@media (min-width: 1024px) {
  body:has(.ss-feedback-cta) .speak { right: 24px; bottom: 70px; }
  body:has(.ss-feedback-cta) .speak-pop { right: 24px; bottom: 134px; }
}
/* On a phone the panel spans the screen above the button. */
@media (max-width: 640px) { .speak-pop { left: 1rem; right: 1rem; width: auto; } }
@media print { .speak { display: none; } }
.hsearch { position: relative; display: flex; align-items: center; }
.hsearch-btn {
  background: none; border: 1px solid var(--ss-border-strong); color: var(--ss-text-subtle);
  border-radius: 6px; padding: 5px 8px; display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; font: inherit; line-height: 1; transition: border-color .15s, color .15s;
}
.hsearch-btn:hover { color: var(--ss-text); }
.hsearch-btn:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: 2px; }
.hsearch.is-open .hsearch-btn { visibility: hidden; }
/* The bar grows out of the button leftward, over whatever the header has there,
   so opening it never reflows the header. */
.hsearch-bar {
  position: absolute; top: 50%; right: 0; z-index: 56; transform: translateY(-50%);
  width: 2.25rem; opacity: 0; pointer-events: none;
  transition: width .18s ease, opacity .12s ease;
}
.hsearch.is-open .hsearch-bar { width: min(22rem, calc(100vw - 2rem)); opacity: 1; pointer-events: auto; }
.hsearch-field {
  display: flex; align-items: center; gap: 0.45rem; box-sizing: border-box; height: 2.25rem;
  padding: 0 0.35rem 0 0.7rem; background: var(--ss-surface); color: var(--ss-text-subtle);
  border: 1px solid var(--ss-border-strong); border-radius: 999px; box-shadow: var(--ss-elev-pop);
}
.hsearch-field:focus-within { border-color: var(--ss-brand); }
.hsearch-field svg { flex: none; }
.hsearch-input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--ss-text);
  font: inherit; font-size: 0.9rem; padding: 0;
}
.hsearch-input::placeholder { color: var(--ss-text-placeholder); }
.hsearch-input::-webkit-search-cancel-button { display: none; }
.hsearch-x {
  flex: none; display: inline-flex; align-items: center; justify-content: center; width: 1.6rem; height: 1.6rem;
  border: 0; border-radius: 999px; background: none; color: var(--ss-text-faint); cursor: pointer; padding: 0;
}
.hsearch-x:hover { color: var(--ss-text); background: var(--ss-surface-hover, var(--ss-border-soft)); }
.hsearch-list {
  position: absolute; top: calc(100% + 6px); right: 0; width: 100%; box-sizing: border-box;
  background: var(--ss-surface); border: 1px solid var(--ss-border); border-radius: 12px; box-shadow: var(--ss-elev-pop);
  padding: 5px; max-height: min(26rem, 70vh); overflow-y: auto; text-align: left;
}
.hsearch-list[hidden] { display: none; }
.people-search-row { display: flex; align-items: center; gap: .6rem; min-width: 0; padding: .55rem .6rem; border-radius: 8px; color: var(--ss-text); text-decoration: none; }
.people-search-row:hover, .people-search-row:focus-visible { background: var(--ss-brand-tint); }
.people-search-body { display: flex; flex-direction: column; min-width: 0; flex: 1; gap: .1rem; }
.people-search-name, .people-search-handle { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.people-search-name { font-size: .88rem; }
.people-search-handle { font-size: .74rem; color: var(--ss-text-faint); }

.dash-suggest-h { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.45rem 0.6rem 0.25rem; font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ss-text-faint); }
.dash-suggest-wait { text-transform: none; letter-spacing: 0; font-weight: 500; }
.dash-suggest-o { display: flex; align-items: center; gap: 0.6rem; padding: 0.45rem 0.6rem; border-radius: 8px; color: var(--ss-text); text-decoration: none; cursor: pointer; min-width: 0; }
.dash-suggest-o[aria-selected="true"] { background: var(--ss-brand-tint); }
.dash-suggest-e { flex: none; width: 1.4rem; text-align: center; font-size: 1rem; line-height: 1; }
.dash-suggest-e svg { display: block; margin: 0 auto; color: var(--ss-text-subtle); }
.dash-suggest-b { display: flex; flex-direction: column; min-width: 0; flex: 1; gap: 0.1rem; }
.dash-suggest-t, .dash-suggest-s { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-suggest-t { font-size: 0.88rem; }
.dash-suggest-t mark { background: transparent; color: inherit; font-weight: 700; }
.dash-suggest-s { font-size: 0.74rem; color: var(--ss-text-faint); }
.dash-suggest-who { font-weight: 600; color: var(--ss-text-subtle); }
.dash-suggest-who.is-mine { color: var(--ss-brand-text); }
.dash-suggest-k { flex: none; font-size: 0.72rem; color: var(--ss-text-faint); }
.dash-suggest-o:not([aria-selected="true"]) .dash-suggest-k { visibility: hidden; }
.dash-suggest-act { border-top: 1px solid var(--ss-border-soft); margin-top: 4px; padding-top: 4px; }
.dash-suggest-act .dash-suggest-t { color: var(--ss-text-muted); }
@media (prefers-reduced-motion: reduce) { .hsearch-bar { transition: none; } }
@media (max-width: 640px) {
  /* On a phone the bar takes the header's row, edge to edge. */
  .hsearch-bar { position: fixed; top: 10px; left: 1rem; right: 1rem; transform: none; width: auto; }
  .hsearch.is-open .hsearch-bar { width: auto; }
}
header.doc-hdr {
  padding: 0.75rem 1.5rem; border-bottom: 1px solid var(--ss-border);
  display: flex; align-items: center; gap: 1rem; flex-shrink: 0;
}
.doc-hdr-logo {
  text-decoration: none; color: var(--ss-text-faint); display: flex; align-items: center; flex-shrink: 0;
}
/* The shared tooltip sits below the logo, aligned to the viewport's left edge.
   A deliberate keyboard focus keeps the component's immediate feedback. */
.doc-hdr-logo[data-tip]::after { left: 0; transform: none; }
@media (hover: hover) and (pointer: fine) {
  .doc-hdr-logo[data-tip]:hover:not(:focus-visible)::after { transition-delay: 1s; }
}
/* The title shrinks to its text (ellipsized when long) rather than filling the bar,
   so anything in the after-title slot sits right beside the name. The tools stay
   pinned right via .hdr-right's margin-left:auto. */
.doc-hdr .title {
  font-weight: 500; flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.doc-hdr .hdr-right { margin-left: auto; }
/* Ownership shows as "@handle /" beside the title. Wide enough, that's the
   clearest form; on a phone the words cost more of the bar than the document's
   own name, so the avatar stands in for them below. Exactly one is ever shown. */
.doc-hdr .doc-owner { flex-shrink: 0; display: inline-flex; align-items: center; color: inherit; text-decoration: none; }
.doc-hdr .doc-owner-handle { color: var(--ss-text-faint); font-weight: 400; }
.doc-hdr a.doc-owner:hover .doc-owner-handle { color: var(--ss-text-muted); text-decoration: underline; text-underline-offset: 3px; }
.doc-hdr .doc-owner-av { display: none; margin-right: 0.4rem; }
.doc-hdr .title-name.editable {
  cursor: text; border-radius: 4px; padding: 2px 4px; margin: -2px -4px;
}
.doc-hdr .title-name.editable:hover { background: var(--ss-surface-soft); }
.doc-hdr .title-input {
  font: inherit; font-weight: 500; color: inherit; background: transparent;
  /* This underline is the whole affordance — the only thing saying the title is
     a field — so it takes the lit brand, which clears 3:1 on a dark ground where
     the brand fill is 2.8:1. Same value as the fill in the light theme. */
  border: none; border-bottom: 1px solid var(--ss-brand-text); border-radius: 0;
  padding: 2px 0; outline: none; width: min(40vw, 28rem); max-width: 100%; box-sizing: border-box;
}
.org-chip {
  display: inline-flex; align-items: center; gap: 0.5rem;
  background: var(--ss-inverse-surface); color: var(--ss-inverse-text);
  padding: 0.28rem 0.7rem 0.28rem 0.32rem; border-radius: 999px;
  text-decoration: none; box-shadow: 0 1px 3px rgba(20, 20, 30, 0.18);
  transition: background 0.15s; flex-shrink: 0;
}
.org-chip:hover { background: var(--ss-inverse-surface-hover); }
.org-chip .ava {
  width: 22px; height: 22px; border-radius: 50%; background: var(--ss-brand); color: var(--ss-inverse-text);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.72rem; font-weight: 700; flex-shrink: 0;
}
.org-chip .lbl { display: flex; flex-direction: column; line-height: 1.12; }
.org-chip .lbl .k {
  font-size: 0.56rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ss-text-faint);
}
.org-chip .lbl .n { font-size: 0.82rem; font-weight: 600; color: var(--ss-inverse-text); white-space: nowrap; }
/* The workspace switcher (workspaceSwitch): where you are — your own spaces or a team —
   and the way to anywhere else, in the same spot on every page. Proportions follow
   design-explorations/workspace-switch-A.reference.html. It rides the account menu's
   <details class="idmenu"> for open/close. */
.wsw { position: relative; flex-shrink: 0; }
html.wsw-lock { overflow: hidden; }
/* No divider between the wordmark and the switcher (2026-10-02): the chip's own
   border already sets it apart, and it sits a little closer than the header's
   gap so the two read as one group. */
.app-hdr > .wsw { margin-left: -0.25rem; }
.wsw > summary { list-style: none; cursor: pointer; }
.wsw > summary::-webkit-details-marker { display: none; }
.wsw > summary::marker { content: ""; }
.wsw-chip { display: inline-flex; align-items: center; gap: 7px; height: 32px; padding: 0 10px 0 4px; border: 1px solid var(--ss-border);
  background: var(--ss-surface); border-radius: 999px; color: var(--ss-text); transition: border-color .15s, box-shadow .15s; }
.wsw-chip:hover { border-color: var(--ss-border-strong); }
.wsw[open] > .wsw-chip { border-color: var(--ss-brand-tint-border); box-shadow: 0 0 0 3px var(--ss-brand-tint); }
.wsw-chip .ss-av { flex: none; }
.wsw-lbl { display: flex; flex-direction: column; line-height: 1.1; min-width: 0; }
.wsw-lbl .k { font-size: 9px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ss-text-subtle); }
.wsw-lbl .n { font-size: 12.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 11rem; }
.wsw-caret { color: var(--ss-text-faint); flex: none; transition: transform .15s; }
.wsw[open] .wsw-caret { transform: rotate(180deg); }
.wsw .wsw-pop { left: 0; right: auto; width: max-content; min-width: 230px; max-width: min(340px, calc(100vw - 24px)); }
.wsw-item > span:not(.ss-av):not(.tick) { min-width: 0; }
.wsw-item { display: flex; align-items: center; gap: 9px; padding: 7px 9px; color: var(--ss-text); white-space: normal; }
.wsw-item:hover { background: var(--ss-surface-muted); color: var(--ss-text); }
.wsw-item:focus-visible, .wsw-note button:focus-visible { outline: 2px solid var(--ss-brand-tint-border); outline-offset: -2px; }
.wsw-item:focus-visible { background: var(--ss-surface-muted); }
.wsw-item[aria-current="true"] { background: var(--ss-brand-tint); }
.wsw-item .t { display: block; font-size: 12.5px; line-height: 1.3; }
.wsw-item small { display: block; font-size: 10.5px; color: var(--ss-text-subtle); line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wsw-item .tick { margin-left: auto; color: var(--ss-brand-text); font-weight: 700; padding-left: 6px; }
.wsw-item.add { color: var(--ss-brand-text); font-weight: 600; font-size: 12.5px; }
.wsw-sep { height: 1px; background: var(--ss-border-soft); margin: 4px 2px; }
.wsw-cap { font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ss-text-subtle); padding: 6px 9px 2px; }
.wsw-note { font-size: 11.5px; color: var(--ss-text-subtle); padding: 4px 9px 6px; margin: 0; }
.wsw-note button { font: inherit; color: var(--ss-brand-text); background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline; }
@media (max-width: 640px) { .wsw-lbl .k { display: none; } .wsw-lbl .n { max-width: 6.5rem; } }

.ss-count-badge{position:absolute;top:-6px;right:-6px;background:var(--ss-brand);color:var(--ss-inverse-text);border-radius:10px;padding:0 4px;font-size:.62rem;font-weight:700;min-width:16px;height:16px;line-height:16px;text-align:center;font-variant-numeric:tabular-nums;box-shadow:0 0 0 2px #f8f7f4;pointer-events:none}
/* Notifications are the one alert among them — red, not brand purple. */
.ss-count-badge.alert{background:var(--ss-danger)}
@media (max-width: 640px) {
  header.doc-hdr { padding: 0.6rem 0.75rem; gap: 0.5rem; }
  header.doc-hdr .doc-owner-handle { display: none; }
  header.doc-hdr .doc-owner-av { display: inline-flex; }
  /* With the handle gone the name can have the slack the tools don't use, instead
     of being cut at a fixed 40vw while empty bar sat beside it. It still shrinks
     first (the tools never do), so a long title ellipsizes rather than pushing
     them off the edge. */
  header.doc-hdr .title {
    font-size: 0.85rem; flex: 1 1 auto; min-width: 0;
    /* The owner avatar lives in here, so the clipping that produces the ellipsis has
       to belong to the name alone — on .title it cuts the face off the moment the
       rename input claims the room. */
    display: flex; align-items: center; overflow: visible;
  }
  header.doc-hdr .title-name {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* Renaming stays available on a phone; it just has to fit. The width takes what the
     name had rather than a fixed 40vw that overshot the bar and scrolled the text out
     of sight, and 16px is the floor below which iOS zooms the page on focus — which is
     what made tapping the title feel like a bug rather than an edit. */
  header.doc-hdr .title-input { width: 100%; font-size: 16px; }
  header.doc-hdr .hdr-right { flex-shrink: 0; }
}

    /* The label is the ::after of the element it describes, so it needs a
       positioning context and needs the element not to clip it — .ss-av rounds
       its photo with overflow:hidden, and would cut the card in half. */
    [data-tip] { position: relative; }
    [data-tip]::after {
      content: attr(data-tip);
      position: absolute; left: 50%; bottom: calc(100% + 4px); transform: translateX(-50%);
      z-index: 30; pointer-events: none;
      /* max-content, then a cap: short labels stay on one line (a name, a handle),
         long ones wrap into a block instead of running off the screen. */
      width: max-content; max-width: min(15rem, 60vw);
      background:var(--ss-surface);color:var(--ss-text);border:1px solid var(--ss-border);border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.1);padding:4px 8px;text-align:center;white-space:normal;
      font-size: 0.7rem; font-weight: 400; line-height: 1.35;
      /* The trigger is often a button with its own font tricks (tabular numerals on
         a reaction count); the label is prose and shouldn't inherit them. */
      font-variant-numeric: normal;
      /* Visibility rather than display, because display can't be delayed. This is
         the resting state AND the way out: no delay here, so the card leaves the
         instant the pointer does. The wait lives on :hover below. */
      opacity: 0; visibility: hidden;
      transition: opacity 0.12s ease, visibility 0s linear 0.12s;
    }
    [data-tip][data-tip-at="below"]::after { bottom: auto; top: calc(100% + 4px); }
    /* Hover only where hovering is a thing — on a touchscreen :hover sticks after a
       tap, which on a reaction chip would leave a label pinned over the message you
       just reacted to. Focus is how the label is reached there, and by keyboard. */
    @media (hover: hover) and (pointer: fine) {
      [data-tip]:hover::after {
        opacity: 1; visibility: visible;
        transition: opacity 0.12s ease 1500ms, visibility 0s linear 1500ms;
      }
    }
    /* Tabbing to something is asking for it. No wait. */
    [data-tip]:focus-visible::after {
      opacity: 1; visibility: visible;
      transition: opacity 0.12s ease, visibility 0s linear;
    }
.ss-dots { display: inline-flex; gap: 3px; flex-shrink: 0; }
.ss-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--ss-brand); animation: ss-dots-walk 1.2s ease-in-out infinite; }
.ss-dots i:nth-child(2) { animation-delay: 0.15s }
.ss-dots i:nth-child(3) { animation-delay: 0.3s }
@keyframes ss-dots-walk { 0% { opacity: .25; transform: translateY(0) } 40% { opacity: 1; transform: translateY(-2px) } 80% { opacity: .25; transform: translateY(0) } 100% { opacity: .25; transform: translateY(0) } }
@keyframes ss-dots-fade { 0% { opacity: .3 } 40% { opacity: 1 } 100% { opacity: .3 } }
@media (prefers-reduced-motion: reduce) { .ss-dots i { animation: ss-dots-fade 2s ease-in-out infinite } }

.coach{position:fixed;z-index:60;max-width:250px;background:var(--ss-inverse-surface);color:var(--ss-inverse-text);border-radius:10px;padding:.6rem .75rem .6rem .8rem;font-size:.78rem;line-height:1.45;box-shadow:0 10px 30px rgba(0,0,0,.22);display:flex;gap:8px;align-items:flex-start;animation:coachIn .25s ease}
.coach::before{content:"";position:absolute;top:-6px;right:18px;width:12px;height:12px;background:var(--ss-inverse-surface);transform:rotate(45deg)}
.coach--left::before{right:auto;left:18px}
.coach b{color:var(--ss-inverse-text)}
.coach button{background:none;border:none;color:var(--ss-inverse-text-subtle);font-size:1rem;line-height:1;cursor:pointer;padding:0;margin-left:2px}
.coach button:hover{color:var(--ss-inverse-text)}
@keyframes coachIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion: reduce){.coach{animation:none}}

.hdr-brand { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.doc-hdr .hdr-brand { gap: 0.4rem; flex-shrink: 0; }
.hdr-agent { display: inline-flex; align-items: center; padding: 6px 3px; margin: -6px -3px; flex-shrink: 0; cursor: default; -webkit-tap-highlight-color: transparent; }
.hdr-agent:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: 3px; border-radius: 4px; }
.hdr-agent[hidden] { display: none; }
.hdr-agent-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.coach--agent { max-width: min(300px, calc(100vw - 16px)); cursor: pointer; -webkit-tap-highlight-color: transparent; }

.coach--agent::before { left: var(--coach-arrow, 18px); right: auto; }

.coach--agent > span { min-width: 0; }
.coach--agent .coach-ask { display: block; margin-top: 2px; color: var(--ss-inverse-text-subtle); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.coach--agent .coach-meta { display: block; margin-top: 2px; color: var(--ss-inverse-text-subtle); }

@media (max-width: 640px) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="submit"]):not([type="button"]):not([type="image"]),
  textarea,
  select { font-size: 16px !important; }
}
    
    /* The initial is dark ink, not white. The eight fallback colours are a bright
       set — a coral, a lime, a butter yellow — and white on them runs 1.4:1 to
       3.5:1, under AA at every avatar size; the same dark ink is 5:1 to 12:1 on all
       eight. The fill is the person's own colour and does not follow the theme, so
       neither does the ink: this is the same object that is dark in both themes. */
    .ss-av { flex-shrink: 0; border-radius: 50%; overflow: hidden; display: inline-flex; align-items: center; justify-content: center; color: var(--ss-deep, #12141a); font-weight: 600; line-height: 1; text-decoration: none; user-select: none; }
    .ss-av img { width: 100%; height: 100%; object-fit: cover; display: block; }
    .ss-av.ss-av-team { border-radius: 28%; }
    .ss-av-xs { width: 22px; height: 22px; font-size: 9px; }
    .ss-av-sm { width: 28px; height: 28px; font-size: 12px; }
    .ss-av-md { width: 32px; height: 32px; font-size: 13px; }
    .ss-av-lg { width: 40px; height: 40px; font-size: 17px; }
    .ss-av-xl { width: 64px; height: 64px; font-size: 27px; }
    .ss-av-xxl { width: 84px; height: 84px; font-size: 35px; }
    @media print {
  body { height: auto !important; display: block !important; }
  header.doc-hdr, .chat, .sidebar, .ver-banner, .nav-loading,
  .share-overlay, .share-dd, .ver-dd, .ver-ov { display: none !important; }
  .main-row { display: block !important; overflow: visible !important; }
  iframe { width: 100% !important; height: auto !important; min-height: 100vh; border: none !important; }
}
    
  textarea[data-ss-grow] { resize: none; overflow-y: hidden; }

    
  .ss-clamp { transition: max-height 220ms cubic-bezier(.4,0,.2,1); }
  .ss-clamp.is-folded { display: -webkit-box; -webkit-box-orient: vertical;
    -webkit-line-clamp: var(--ss-clamp-lines, 10);
    line-clamp: var(--ss-clamp-lines, 10); overflow: hidden; }
  .ss-clamp-more { margin-top: 3px; padding: 0; background: none; border: none; font: inherit;
    font-size: .72rem; font-weight: 600; line-height: 1.4; color: var(--ss-brand-text);
    cursor: pointer; text-decoration: underline; }
  .ss-clamp-more:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: 2px; border-radius: 3px; }
  @media (prefers-reduced-motion: reduce) { .ss-clamp { transition: none; } }

    
  .ss-fold-row { transition: max-height 200ms cubic-bezier(.4,0,.2,1); }
  .ss-fold-row [data-ss-fold-hid] { display: none !important; }
  .ss-fold-more { font-family: inherit; cursor: pointer; white-space: nowrap; }
  .ss-fold-row.is-foldable .ss-fold-head { cursor: pointer; user-select: none; }
  .ss-fold-row.is-foldable .ss-fold-head::after { content: ""; display: inline-block; width: 4px; height: 4px;
    margin: 0 2px 2px 5px; vertical-align: middle; border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor; transform: rotate(45deg);
    transition: transform 200ms cubic-bezier(.4,0,.2,1); }
  .ss-fold-row.is-foldable.is-open .ss-fold-head::after { transform: translateY(2px) rotate(-135deg); }
  .ss-fold-row.is-foldable .ss-fold-head:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: 2px; border-radius: 3px; }
  @media (prefers-reduced-motion: reduce) {
    .ss-fold-row, .ss-fold-row.is-foldable .ss-fold-head::after { transition: none; }
  }

    .main-row{flex:1;display:flex;overflow:hidden}
    iframe{flex:1;border:none;background:#fff;min-width:0}
    /* Left AI chat drawer — three states: a few-pixel sliver at rest, a flyout tab
       on hover, the full panel on click. Only the sliver is in flow, so revealing
       the tab floats it over the document instead of reflowing the iframe. */
    .chat{display:flex;flex-shrink:0;position:relative}
    .chat-handle{width:7px;flex-shrink:0;position:relative;background:var(--ss-surface);border-right:1px solid var(--ss-border);cursor:pointer;-webkit-user-select:none;user-select:none}
    /* The tab is a control, not copy: nothing in it selects, on a hold or a drag. */
    .chat-flyout,.chat-flyout *{-webkit-user-select:none;user-select:none}
    /* A 7px sliver is a fine resting state and a poor thing to aim at, so bringing
       the cursor near the left edge floats the tab out (.peek). The proximity is
       reported by the document itself — anchor.js in the frame posts ss-edge — not
       sensed by an invisible strip over the page: a strip that can feel the cursor
       is a strip that eats the clicks and text selections belonging to the
       document under it. Cursor devices only; the frame doesn't report on touch,
       where the tab stays out anyway (the hover:none rule below). */
    .chat.peek .chat-handle::after{background:var(--ss-brand);height:64px}
    .chat.peek .chat-flyout{opacity:1;transform:translateY(-50%) translateX(0);pointer-events:auto}
    .chat-handle::after{content:"";position:absolute;left:2px;top:50%;transform:translateY(-50%);width:3px;height:36px;border-radius:2px;background:var(--ss-border-strong);transition:background .15s,height .15s}
    .chat-handle:hover::after{background:var(--ss-brand);height:64px}
    .chat.open .chat-handle::after{background:var(--ss-brand)}
    .chat-flyout{position:absolute;left:0;top:50%;transform:translateY(-50%) translateX(-100%);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:16px 8px;background:var(--ss-surface);border:1px solid var(--ss-border);border-left:none;border-radius:0 10px 10px 0;box-shadow:2px 2px 12px rgba(0,0,0,.1);color:var(--ss-brand-text);opacity:0;pointer-events:none;transition:opacity .16s ease,transform .16s ease;z-index:30}
    .chat-handle:hover .chat-flyout{opacity:1;transform:translateY(-50%) translateX(0);pointer-events:auto}
    .chat.open .chat-flyout{display:none}
    .chat-vt{writing-mode:vertical-rl;transform:rotate(180deg);font-size:.72rem;font-weight:600;letter-spacing:.07em;text-transform:uppercase}
    /* The Co-shepherd is working on this page (.is-running: a turn in this panel, or
       the room's status): with the drawer folded, a brand line runs down its edge —
       the only sign of a run, now that no card floats over the page
       (design-explorations/coshepherd-working-in-drawer.html, variant C). The tab
       shows a spinner when it is out. Reduced motion keeps a still brand grip and a
       fading ring — a frozen ring reads as a stalled one. */
    .chat.is-running:not(.open) .chat-handle::before{content:"";position:absolute;left:2px;top:0;bottom:0;width:3px;border-radius:2px;background:linear-gradient(transparent,var(--ss-brand) 30%,var(--ss-brand) 70%,transparent) no-repeat;background-size:100% 160px;animation:chat-edge-run 1.8s ease-in-out infinite;pointer-events:none}
    .chat.is-running:not(.open) .chat-handle:not(:hover)::after{opacity:0}
    .chat.is-running .chat-handle::after{background:var(--ss-brand)}
    @keyframes chat-edge-run{0%{background-position:0 -160px}100%{background-position:0 calc(100% + 160px)}}
    @media (prefers-reduced-motion: reduce){.chat.is-running:not(.open) .chat-handle::before{display:none}.chat.is-running:not(.open) .chat-handle:not(:hover)::after{opacity:1;height:64px}}
    .chat.is-running .chat-flyout>svg{display:none}
    .chat-spin{display:none;flex-shrink:0;width:18px;height:18px;border:2px solid rgba(90,90,230,.25);border-top-color:var(--ss-brand);border-radius:50%;animation:chat-spin .6s linear infinite}
    .chat.is-running .chat-spin{display:block}
    @keyframes chat-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
    @keyframes chat-spin-fade{0%{opacity:1}50%{opacity:.3}100%{opacity:1}}
    @media (prefers-reduced-motion: reduce){.chat-spin{animation:chat-spin-fade 1.4s ease-in-out infinite}}
    /* Nothing to hover with (touch/pen): leave the tab out permanently. */
    @media(hover:none){
      .chat-flyout{opacity:1;transform:translateY(-50%) translateX(0);pointer-events:auto}
    }
    /* Pure clip mask: only width animates. Panel body lives on an absolute
       inner locked at 360px so text never reflows — collapse wipes from the right. */
    .chat-panel{width:0;min-width:0;overflow:hidden;transition:width .2s;align-self:stretch;position:relative;border:none;background:transparent}
    .chat.open .chat-panel{width:var(--chat-w,360px)}
    .chat-panel-inner{position:absolute;top:0;left:0;bottom:0;width:var(--chat-w,360px);min-width:var(--chat-w,360px);max-width:var(--chat-w,360px);display:flex;flex-direction:column;background:var(--ss-surface);border-right:1px solid var(--ss-border);box-sizing:border-box}
    .chat-hdr{padding:.75rem 1rem;border-bottom:1px solid var(--ss-border);display:flex;align-items:center;gap:.5rem;flex-shrink:0;position:relative}
    /* While it works, a line moves under the header, and Stop sits beside the fold. */
    .chat.is-running .chat-hdr::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:2px;z-index:2;background:linear-gradient(90deg,transparent,var(--ss-brand),transparent) no-repeat var(--ss-brand-tint);background-size:30% 100%;animation:chat-hdr-run 1.4s ease-in-out infinite}
    @keyframes chat-hdr-run{0%{background-position:-50% 0}100%{background-position:150% 0}}
    @media (prefers-reduced-motion: reduce){.chat.is-running .chat-hdr::after{animation:none;background:var(--ss-brand)}}
    .chat-hdr .chat-hstop[hidden]{display:none}
    .chat-hdr .chat-hstop{margin:0;flex-shrink:0;color:var(--ss-danger-text);border-color:var(--ss-danger-tint-border);font-weight:600}
    .chat-hdr .chat-hstop:hover{color:var(--ss-danger-text);border-color:var(--ss-danger-text);background:var(--ss-danger-tint)}
    .chat-hdr .chat-hstop:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .chat-hdr h3{font-size:.9rem;font-weight:600}
    .chat-ints{padding:.5rem 1rem;border-bottom:1px solid var(--ss-border-soft);display:flex;flex-wrap:wrap;gap:.35rem;align-items:center;flex-shrink:0}
    .chat-ints:empty{display:none}
    .chat-ints .lead{font-size:.66rem;color:var(--ss-text-faint);font-weight:600;margin-right:.1rem}
    .chat-ints .ci{font-size:.68rem;padding:2px 7px;border-radius:999px;border:1px solid var(--ss-border);color:var(--ss-text-faint);background:var(--ss-surface);display:inline-flex;align-items:center;gap:4px;text-decoration:none}
    .chat-ints .ci.on{color:var(--ss-success);border-color:var(--ss-success-tint-border);background:var(--ss-success-tint)}
    .chat-ints .ci .d{width:5px;height:5px;border-radius:50%;background:var(--ss-border-strong)}
    .chat-ints .ci.on .d{background:var(--ss-success)}
    .chat-ints a.ci:hover{border-color:var(--ss-border-strong);color:var(--ss-text-subtle)}
    /* Same fold as the dashboard's agent card — connected sources are the working
       set, and a wall of greyed-out names buried them. Skin only; the rule and the
       wiring come from components/connect-more. */
    [data-connect-more]:not(.more-open) .chip-src-off { display: none; }
    .chat-ints .chip-more{font-size:.68rem;padding:2px 8px;border-radius:999px;border:1px solid var(--ss-brand-tint-border);background:var(--ss-brand-tint);color:var(--ss-brand-text);cursor:pointer;font-family:inherit;line-height:1.55}
    .chat-ints .chip-more:hover{border-color:var(--ss-brand-tint-border)}
    .chat-ints .chip-more:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    /* Nine connected sources wrapped to five lines above the conversation, so the
       row folds to its first line (assets/behaviors/fold-row): the label, the chips
       that fit, and a neutral "+N more" — it counts more of the same, it isn't a
       call to action like the violet pill above. */
    .chat-ints .ss-fold-more{font-size:.68rem;padding:2px 8px;border-radius:999px;border:1px solid var(--ss-border);background:var(--ss-surface);color:var(--ss-text-subtle);line-height:inherit}
    .chat-ints .ss-fold-more:hover{border-color:var(--ss-border-strong);color:var(--ss-text-muted)}
    .chat-ints .ss-fold-more:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .chat-ints.is-foldable .lead:hover{color:var(--ss-text-subtle)}
    /* Collapse, not close: the drawer folds back into the edge it came from, so the
       glyph points there (design-explorations/drawer-collapse.html, variant A). */
    .chat-close{width:30px;height:30px;flex-shrink:0;display:grid;place-items:center;padding:0;background:none;border:none;border-radius:8px;color:var(--ss-text-faint);cursor:pointer;margin:-4px 0 -4px auto}
    .chat-close svg{transition:transform .15s}
    .chat-close:hover{background:var(--ss-surface-soft);color:var(--ss-text)}
    .chat-close:hover svg{transform:translateX(-2px)}
    .chat-close:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .chat-scrim{display:none}
    .chat-msgs{flex:1;overflow-y:auto;overscroll-behavior:contain;padding:.75rem .75rem 1.6rem;display:flex;flex-direction:column;gap:.55rem}
    .chat-msgs::-webkit-scrollbar{width:4px}
    .chat-msgs::-webkit-scrollbar-thumb{background:var(--ss-border-strong);border-radius:2px}
    .chat-msg{position:relative;font-size:.82rem;line-height:1.5;word-break:break-word;white-space:pre-wrap}
    /* The user row is a column, not the bubble itself — the bubble skin moved onto
       .chat-bubble so an action row can sit outside the purple, under it. */
    .chat-msg.user{align-self:flex-end;max-width:88%;display:flex;flex-direction:column;align-items:flex-end}
    .chat-msg.user .chat-bubble{background:var(--ss-brand);color:var(--ss-inverse-text);padding:.5rem .7rem;border-radius:12px 12px 3px 12px}
    .chat-msg.assistant{align-self:flex-start;color:var(--ss-text-muted);max-width:95%;white-space:normal}
    .chat-msg.assistant .chat-who{font-size:.62rem;font-weight:700;letter-spacing:.06em;color:var(--ss-brand-text);margin-bottom:2px}
    /* The author's original brief, pinned as the thread's first message (see origin below). */
    .chat-msg.user .chat-origin{font-size:.62rem;font-weight:700;letter-spacing:.06em;color:var(--ss-text-subtle);margin-bottom:2px;text-transform:uppercase}
    /* A brief can be a page of its own, so it folds — ss-clamp does the folding,
       the measuring and the animation (assets/behaviors/clamp-lines.ts). The panel only
       says how the toggle sits in a bubble's column. */
    .chat-more{margin-top:0}
    .chat-md p{margin:0 0 .5rem}
    .chat-md p:last-child{margin-bottom:0}
    .chat-md ul,.chat-md ol{margin:.25rem 0 .5rem;padding-left:1.25rem}
    .chat-md li{margin:.15rem 0}
    .chat-md strong{font-weight:600}
    .chat-md a{color:var(--ss-brand-text)}
    .chat-md code{background:var(--ss-surface-soft);border-radius:4px;padding:.05em .35em;font-size:.9em;font-family:'SF Mono','Fira Code',monospace}
    .chat-md pre{background:var(--ss-surface-muted);border:1px solid var(--ss-border);border-radius:8px;padding:.5rem .6rem;overflow-x:auto;margin:.4rem 0;white-space:pre}
    .chat-md pre code{background:none;padding:0}
    .chat-md .md-h{font-weight:600;margin:.3rem 0}
    .chat-empty{color:var(--ss-text-faint);font-size:.82rem;text-align:center;padding:1.75rem .75rem;line-height:1.55}
    .chat-empty a{color:var(--ss-brand-text)}
    .chat-cta{display:inline-block;background:var(--ss-brand);color:var(--ss-inverse-text)!important;text-decoration:none;padding:8px 16px;border-radius:8px;font-size:.82rem;font-weight:500;transition:background .15s}
    .chat-cta:hover{background:var(--ss-brand-hover)}
    /* First run on a blank document. Left-aligned and full width, unlike the
       centred .chat-empty hint: this is something to read and act on, not a
       caption under a thread. */
    .chat-intro{padding:1.5rem .25rem .5rem;text-align:left}
    .chat-intro h4{font-size:1rem;font-weight:600;color:var(--ss-text);margin-bottom:.35rem}
    .chat-intro p{font-size:.8rem;color:var(--ss-text-subtle);line-height:1.5;margin-bottom:1rem}
    .chat-egs{display:flex;flex-direction:column;gap:.4rem}
    .chat-eg{display:block;width:100%;text-align:left;font:inherit;font-size:.8rem;color:var(--ss-text-muted);background:var(--ss-surface-muted);border:1px solid var(--ss-border);border-radius:8px;padding:.55rem .7rem;cursor:pointer;transition:border-color .15s,background .15s,color .15s}
    .chat-eg:hover{border-color:var(--ss-brand);background:var(--ss-surface);color:var(--ss-brand-text)}
    @media (prefers-reduced-motion:reduce){.chat-eg{transition:none}}
    .chat-typing{display:flex;align-items:center;gap:.5rem;color:var(--ss-text-faint);font-size:.8rem}
    /* Per-message actions (copy, for now). Held back until the message is hovered or
       something inside it takes focus, so a thread at rest is just the conversation;
       a coarse pointer has no hover to give, so there they stay put.

       Absolute, not in flow: a row that reserved its own height would push every
       message apart by the height of a control that is invisible most of the time,
       which is the thread paying rent for it on every turn. Out of flow it costs
       nothing at rest and, on hover, paints into the gap below its own message —
       on the side that message is on, so the two roles never collide. */
    .chat-acts{position:absolute;top:calc(100% + 1px);left:-3px;z-index:2;display:flex;gap:.2rem;opacity:0;transition:opacity .12s}
    .chat-acts--end{left:auto;right:-3px}
    .chat-msg:hover .chat-acts,.chat-msg:focus-within .chat-acts{opacity:1}
    @media (hover:none){.chat-acts{opacity:1}}
    /* A message that folds has one permanently visible control — "Show more" —
       and it belongs on the row the copy button already occupies, not on a line
       of its own above it. So that row comes back into flow (it now has height
       worth paying for), shows itself, and the copy button alone keeps the
       hover-only behaviour it has everywhere else. The row spans the bubble:
       the toggle opens it and the copy still closes it at the far edge, where it
       has always been. */
    .chat-acts--row{position:static;opacity:1;align-items:center;justify-content:space-between;width:100%;margin-top:3px}
    .chat-msg.other .chat-acts--row{grid-column:2}
    .chat-acts--row .ss-copy{opacity:0;transition:opacity .12s}
    .chat-msg:hover .chat-acts--row .ss-copy,.chat-msg:focus-within .chat-acts--row .ss-copy{opacity:1}
    @media (hover:none){.chat-acts--row .ss-copy{opacity:1}}
    .chat-apply{display:flex;gap:.4rem;align-items:center;margin-top:.5rem}
    .chat-apply button{background:var(--ss-success);color:var(--ss-inverse-text);border:none;border-radius:6px;padding:5px 12px;font-size:.78rem;cursor:pointer;font-weight:500}
    .chat-apply button:hover{background:var(--ss-success)}
    .chat-apply button:disabled{opacity:.55;cursor:default}
    .chat-apply .discard{background:none;color:var(--ss-text-faint);border:1px solid var(--ss-border-strong)}
    .chat-apply .discard:hover{background:var(--ss-surface-soft);color:var(--ss-text-muted)}
    .chat-apply .chat-saved{color:var(--ss-success-text);font-size:.78rem;font-weight:500}
    .chat-apply .chat-savefail{color:var(--ss-warn-text);font-size:.75rem}
    .chat-retry{margin-top:.45rem}
    .chat-retry button{background:var(--ss-surface);color:var(--ss-brand-text);border:1px solid var(--ss-brand-tint-border);border-radius:6px;padding:4px 10px;font-family:inherit;font-size:.75rem;font-weight:600;cursor:pointer}
    .chat-retry button:hover{background:var(--ss-brand-tint);border-color:var(--ss-brand-tint-border)}
    .chat-retry button:disabled{opacity:.6;cursor:default}
    .chat-retry button:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .chat-note{color:var(--ss-brand-text);font-size:.75rem;margin-top:.35rem}
    .chat-stop{display:inline-block;margin-top:.4rem;padding:.15rem .55rem;border:1px solid var(--ss-border);border-radius:999px;background:var(--ss-surface);color:var(--ss-text-subtle);font-size:.72rem;cursor:pointer}
    .chat-stop:hover{border-color:var(--ss-border-strong);color:var(--ss-text)}
    .chat-stop:disabled{opacity:.5;cursor:default}
    /* A run's steps in the thread: each one said, with how long it took, then the one
       in hand with the run's clock. Done, they fold into one line above the answer. */
    /* The steps' times line up at the right edge, however short the words. */
    .chat-msg.assistant:has(.chat-work,.chat-worked){align-self:stretch}
    .chat-work{display:flex;flex-direction:column;gap:.3rem;margin:.1rem 0 .35rem}
    .chat-wstep{display:flex;align-items:flex-start;gap:.5rem;font-size:.78rem;line-height:1.35;color:var(--ss-text-subtle)}
    .chat-wstep .wtx{flex:1;min-width:0;overflow-wrap:anywhere}
    .chat-wstep .wd{flex-shrink:0;font-size:.72rem;color:var(--ss-text-faint);font-variant-numeric:tabular-nums}
    .chat-wstep.is-now{color:var(--ss-brand-text);font-weight:600}
    .chat-wstep.is-now .wd{color:var(--ss-text-subtle);font-weight:500}
    .chat-wstep .ss-dots{margin-top:.38em;width:17px}
    .wck{flex-shrink:0;width:16px;height:16px;border-radius:50%;display:grid;place-items:center;background:var(--ss-success-tint);color:var(--ss-success-text);margin-top:.05em}
    .chat-wquiet{font-size:.72rem;color:var(--ss-text-faint);padding-left:1.55rem}
    .chat-worked{margin:0 0 .45rem;border:1px solid var(--ss-border);border-radius:10px;background:var(--ss-surface-soft)}
    .chat-worked summary{list-style:none;display:flex;align-items:center;gap:.5rem;padding:.4rem .6rem;font-size:.75rem;color:var(--ss-text-faint);cursor:pointer;border-radius:10px}
    .chat-worked summary::-webkit-details-marker{display:none}
    .chat-worked summary:hover{color:var(--ss-text-subtle)}
    .chat-worked summary:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .chat-worked summary b{color:var(--ss-text-subtle);font-weight:600}
    .chat-worked .wchev{margin-left:auto;display:flex;transition:transform .15s}
    .chat-worked[open] .wchev{transform:rotate(90deg)}
    .chat-wlist{display:flex;flex-direction:column;gap:.3rem;padding:.5rem .6rem .55rem;border-top:1px solid var(--ss-border-soft)}
    @media (prefers-reduced-motion: reduce){.chat-worked .wchev{transition:none}}
    .chat-steps{display:flex;flex-direction:column;gap:3px;margin-bottom:.4rem;padding:.35rem .5rem;background:var(--ss-brand-tint);border:1px solid var(--ss-brand-tint-border);border-radius:8px}
    .chat-step{font-size:.72rem;color:var(--ss-text-subtle);display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
    .chat-step .cs-ic{flex-shrink:0;width:1em;text-align:center}
    .chat-step .cs-int{font-weight:600;color:var(--ss-brand-deep)}
    .chat-step code{font-family:'SF Mono','Fira Code',monospace;font-size:.68rem;color:var(--ss-brand-text);background:var(--ss-brand-tint);padding:0 4px;border-radius:3px;word-break:break-all}
    .chat-step .cs-err{color:var(--ss-danger)}
    
.ss-caret { display: inline-block; width: 7px; height: 13px; background: #9a9ae6; border-radius: 2px; vertical-align: text-bottom; margin-left: 2px; animation: ss-caret-blink 1s steps(2) infinite; }
@keyframes ss-caret-blink { 0% { opacity: 1 } 50% { opacity: 0.15 } 100% { opacity: 1 } }
@keyframes ss-caret-fade { 0% { opacity: 1 } 50% { opacity: 0.3 } 100% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) { .ss-caret { animation: ss-caret-fade 2s ease-in-out infinite } }

    
    .chat-compose{padding:.6rem .75rem;border-top:1px solid var(--ss-border);flex-shrink:0}
    /* Toggle on the left, model picker pushed to the right — both live above the
       input so the header carries nothing but the title. */
    .chat-toolrow{display:flex;align-items:center;gap:8px;margin-bottom:8px;min-width:0}
    .chat-editrow{display:flex;align-items:center;gap:6px;font-size:.72rem;color:var(--ss-text-faint);cursor:pointer;flex-shrink:0}
    .chat-toolrow .ss-pill-select{margin-left:auto}
    /* .chat-panel is a clip mask (the collapsing drawer wipes from the right), so a
       menu anchored left would be cut off at the panel edge — drop it from the right. */
    .chat-toolrow .ss-pill-select-menu{left:auto;right:0}
    /* The composer is a pill (.ss-pill-field, send-button.ts); the paperclip sits
       close to the words so the field keeps its width. */
    .chat-inrow.ss-pill-field textarea{padding-left:2px}
    /* The room: the thread's participants in the header, other people's messages on
       the left with their face, versions from elsewhere as one line, and the strip
       that says whose turn is running while the composer waits. */
    /* The faces take the header's free space alone; the close button's own auto
       margin would otherwise split it and leave the faces floating mid-header. */
    .chat-faces{display:flex;align-items:center;margin-left:auto;margin-right:.4rem;min-height:28px}
    .chat-hdr .chat-close{margin-left:0}
    .chat-faces .ss-av{border:2px solid var(--ss-surface);margin-left:-6px}
    .chat-faces .ss-av:first-child{margin-left:0}
    .chat-msg.other{align-self:flex-start;max-width:88%;display:grid;grid-template-columns:28px 1fr;column-gap:8px}
    .chat-msg.other .ss-av{grid-row:1/3;align-self:end;margin-bottom:2px}
    .chat-msg.other .chat-who{font-size:.62rem;font-weight:700;letter-spacing:.06em;color:var(--ss-text-subtle);margin-bottom:2px;text-transform:uppercase}
    .chat-msg.other .chat-who .via{color:var(--ss-text-faint);text-transform:none;letter-spacing:.04em}
    .chat-msg.other .chat-bubble{background:var(--ss-surface-soft);color:var(--ss-text);padding:.5rem .7rem;border-radius:12px 12px 12px 3px;white-space:pre-wrap}
    .chat-msg.assistant .chat-who .to{color:var(--ss-text-faint);font-weight:600;letter-spacing:.04em;margin-left:.35em}
    .chat-event{align-self:stretch;display:grid;grid-template-columns:28px 1fr auto;column-gap:8px;align-items:center;font-size:.72rem;color:var(--ss-text-subtle);padding:.15rem 0}
    .chat-event .ss-av{grid-row:1/3}
    .chat-event b{color:var(--ss-text-muted);font-weight:600}
    .chat-event .ev-ver{grid-column:2;color:var(--ss-success);font-weight:600;font-size:.68rem}
    .chat-event .ev-when{grid-column:3;grid-row:1;font-size:.64rem;color:var(--ss-text-faint);font-variant-numeric:tabular-nums}
    .chat-event .ev-sess{margin-left:.5rem;color:var(--ss-brand-text);font-weight:600;text-decoration:none;white-space:nowrap}
    .chat-event .ev-sess:hover{text-decoration:underline}
    .chat-event .ev-sess:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px;border-radius:3px}
    .chat-named{grid-template-columns:28px minmax(0,1fr) auto;row-gap:1px}
    .chat-named .nm-ic{grid-row:1/3;display:inline-flex;justify-content:center;color:var(--ss-text-faint)}
    .chat-named .nm-txt{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .chat-named .ss-copy{grid-column:3;grid-row:1/3}
    .chat-named .nm-url{grid-column:2;min-width:0;overflow-wrap:anywhere;color:var(--ss-brand-text);text-decoration:none;font-weight:600}
    .chat-named .nm-url:hover{text-decoration:underline}
    .chat-named .nm-url:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px;border-radius:3px}
    .chat-turn{display:flex;align-items:center;gap:8px;padding:6px 8px;margin-bottom:8px;background:var(--ss-brand-tint);border:1px solid var(--ss-brand-tint-border);border-radius:8px;font-size:.74rem;color:var(--ss-text-muted);min-width:0}
    .chat-turn[hidden]{display:none}
    .chat-turn .spin{width:14px;height:14px;border:2px solid rgba(90,90,230,.25);border-top-color:var(--ss-brand);border-radius:50%;animation:chat-spin .6s linear infinite;flex-shrink:0}
    .chat-turn .who{font-weight:600}
    /* Name and ask share one block that wraps to two lines, so a long name does
       not squeeze the ask down to "is wor…". */
    .chat-turn .txt{flex:1;min-width:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;line-height:1.35}
    .chat-turn .ask{color:var(--ss-text-subtle)}
    .chat-turn .chat-stop{background:none;border:1px solid var(--ss-brand-tint-border);color:var(--ss-brand-text);border-radius:6px;padding:2px 8px;font-size:.7rem;font-weight:600;font-family:inherit;cursor:pointer;flex-shrink:0}
    /* Files on a message (space-chat-files.ts): pictures as tiles, anything else as a
       chip with its kind, above the words; in the composer, a chip per file while it
       uploads and waits; over the whole panel, the drop target while files are dragged. */
    .chat-body{display:flex;flex-direction:column;align-items:flex-start;gap:4px;min-width:0}
    .chat-att{display:flex;flex-wrap:wrap;gap:4px;justify-content:flex-end;max-width:100%}
    .chat-msg.other .chat-att{justify-content:flex-start}
    .chat-att + .chat-bubble{margin-top:4px}
    .chat-att-img{display:block;line-height:0;border-radius:10px;overflow:hidden;border:1px solid var(--ss-border);background:var(--ss-surface-soft)}
    .chat-att-img img{display:block;max-width:220px;max-height:220px;width:auto;height:auto}
    .chat-att.many .chat-att-img img{width:96px;height:96px;max-width:none;max-height:none;object-fit:cover}
    .chat-att-img:focus-visible,.chat-att-file:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .chat-att-file{display:inline-flex;align-items:center;gap:6px;max-width:240px;padding:6px 10px 6px 6px;border:1px solid var(--ss-border);border-radius:10px;background:var(--ss-surface);color:var(--ss-text);font-size:.74rem;line-height:1.3;text-decoration:none;white-space:nowrap}
    .chat-att-file:hover{border-color:var(--ss-border-strong);background:var(--ss-surface-soft)}
    .chat-att-file::before{content:attr(data-badge);flex-shrink:0;font-size:.6rem;font-weight:700;letter-spacing:.04em;padding:3px 5px;border-radius:5px;background:var(--ss-brand-tint);color:var(--ss-brand-text)}
    .chat-att-file .nm{min-width:0;overflow:hidden;text-overflow:ellipsis}
    .chat-files{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}
    .chat-files[hidden]{display:none}
    .chat-fchip{position:relative;display:flex;align-items:center;gap:6px;max-width:100%;height:40px;padding:0 26px 0 4px;border:1px solid var(--ss-border-strong);border-radius:8px;background:var(--ss-surface-soft);font-size:.72rem;color:var(--ss-text-muted);box-sizing:border-box}
    .chat-fchip img{width:30px;height:30px;border-radius:5px;object-fit:cover;display:block;flex-shrink:0}
    .chat-fchip .chat-fic{flex-shrink:0;font-size:.58rem;font-weight:700;letter-spacing:.04em;padding:3px 5px;border-radius:5px;background:var(--ss-brand-tint);color:var(--ss-brand-text)}
    .chat-fchip .chat-fname{max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .chat-fchip.up img,.chat-fchip.up .chat-fname{opacity:.6}
    .chat-fspin{flex-shrink:0;width:12px;height:12px;border:2px solid var(--ss-border-strong);border-top-color:var(--ss-brand);border-radius:50%;animation:chat-spin .6s linear infinite;box-sizing:border-box}
    .chat-fx{position:absolute;top:50%;right:4px;transform:translateY(-50%);width:18px;height:18px;border:none;border-radius:50%;background:transparent;color:var(--ss-text-faint);cursor:pointer;font-size:1rem;line-height:18px;padding:0;text-align:center}
    .chat-fx:hover{color:var(--ss-text);background:var(--ss-surface-muted)}
    .chat-fx:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .chat-file-err{font-size:.72rem;line-height:1.4;color:var(--ss-danger);margin-bottom:6px}
    .chat-file-err[hidden]{display:none}
    .chat-attach{flex-shrink:0;width:30px;height:38px;border:none;border-radius:14px;background:none;color:var(--ss-text-subtle);display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0;transition:color .15s,background .15s}
    .chat-attach:hover:not(:disabled){color:var(--ss-brand-text);background:var(--ss-surface-soft)}
    .chat-attach:disabled{opacity:.45;cursor:default}
    .chat-attach:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .chat-file-input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
    .chat-drop{position:absolute;inset:8px;z-index:6;display:flex;align-items:center;justify-content:center;border:2px dashed var(--ss-brand);border-radius:12px;background:var(--ss-surface);opacity:.95;color:var(--ss-brand-text);font-weight:600;font-size:.88rem;pointer-events:none}
    .chat-drop[hidden]{display:none}
    .chat-drop span{display:flex;flex-direction:column;align-items:center;gap:8px}
    [data-lightbox] { cursor: zoom-in; }
.ss-lb { position: fixed; inset: 0; z-index: 1100; display: grid; place-items: center; padding: 1.5rem; background: var(--ss-veil, rgba(248, 247, 244, 0.9)); cursor: zoom-out; }
.ss-lb[hidden] { display: none; }
.ss-lb img { display: block; max-width: min(100%, 1120px); max-height: calc(100vh - 3rem); object-fit: contain; border-radius: 10px; box-shadow: 0 14px 42px var(--ss-shadow, rgba(0, 0, 0, 0.32)); cursor: default; background: var(--ss-surface); }
.ss-lb-close { position: fixed; top: 0.8rem; right: 0.9rem; width: 36px; height: 36px; border: 1px solid var(--ss-border); border-radius: 50%; background: var(--ss-surface); color: var(--ss-text); font: inherit; font-size: 1.55rem; line-height: 1; cursor: pointer; }
.ss-lb-close:hover, .ss-lb-close:focus-visible { background: var(--ss-surface-muted); outline: none; }
.ss-lb img[hidden], .ss-lb-file[hidden] { display: none; }
.ss-lb-file { width: min(100%, 1120px); height: calc(100dvh - 5rem); min-width: 0; display: flex; flex-direction: column; background: var(--ss-surface); color: var(--ss-text); border: 1px solid var(--ss-border); border-radius: 12px; overflow: hidden; cursor: default; }
.ss-lb-toolbar { display: flex; align-items: center; gap: 1rem; padding: 0.85rem 1rem; border-bottom: 1px solid var(--ss-border); }
.ss-lb-title { flex: 1; min-width: 0; overflow-wrap: anywhere; font-weight: 600; }
.ss-lb-download { color: var(--ss-brand-text); flex: none; }
.ss-lb-content { flex: 1; min-height: 0; overflow: auto; }
.ss-lb-content pre { margin: 0; padding: 1rem; white-space: pre-wrap; overflow-wrap: anywhere; font: 0.9rem/1.6 monospace; }
.ss-lb-content iframe { width: 100%; height: 100%; border: 0; background: white; }
.ss-lb-status { padding: 1rem; }
@media (max-width: 600px) { .ss-lb { padding: 3.5rem 0.5rem 0.5rem; } .ss-lb-file { height: calc(100dvh - 4rem); } }
body.ss-lb-open { overflow: hidden; }
    /* Messages sent while a turn runs, waiting their go (viewer-coshepherd.ts):
       a quiet list above the box, each with Edit and remove. */
    .chat-queue{margin-bottom:8px;padding:6px 8px 4px;border:1px solid var(--ss-border);border-radius:10px;background:var(--ss-surface-soft);font-size:.74rem;color:var(--ss-text-muted);min-width:0}
    .chat-queue[hidden]{display:none}
    .chat-q-head{display:flex;align-items:center;gap:8px;min-height:20px;font-size:.66rem;font-weight:600;letter-spacing:.02em;color:var(--ss-text-subtle)}
    .chat-q-say{flex:1;min-width:0}
    .chat-q-go{flex-shrink:0;background:none;border:1px solid var(--ss-brand-tint-border);color:var(--ss-brand-text);border-radius:6px;padding:1px 8px;font:inherit;font-size:.68rem;font-weight:600;cursor:pointer}
    .chat-q-go:hover{background:var(--ss-brand-tint)}
    .chat-q-list{list-style:none;margin:2px 0 0;padding:0;display:flex;flex-direction:column}
    .chat-q{display:flex;align-items:center;gap:6px;min-width:0;padding:3px 0}
    .chat-q + .chat-q{border-top:1px solid var(--ss-border-soft)}
    .chat-q svg{flex-shrink:0;color:var(--ss-text-faint)}
    .chat-q-txt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ss-text)}
    .chat-q-n{flex-shrink:0;font-size:.66rem;color:var(--ss-text-faint)}
    .chat-q-b{flex-shrink:0;background:none;border:none;color:var(--ss-brand-text);font:inherit;font-size:.7rem;font-weight:600;cursor:pointer;padding:2px 5px;border-radius:5px}
    .chat-q-b:hover{background:var(--ss-surface-muted)}
    .chat-q-x{flex-shrink:0;width:20px;height:20px;border:none;border-radius:50%;background:transparent;color:var(--ss-text-faint);cursor:pointer;font-size:1rem;line-height:20px;padding:0;text-align:center}
    .chat-q-x:hover{color:var(--ss-text);background:var(--ss-surface-muted)}
    .chat-q-go:focus-visible,.chat-q-b:focus-visible,.chat-q-x:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .hdr-right{display:flex;align-items:center;gap:.75rem;margin-left:auto}
    .pres-wrap{display:flex;align-items:center}
    .presence{display:flex;align-items:center}
    /* Only ever seen once the wrapper has been moved into the ⋯ menu. */
    .pres-label{display:none}
    /* Presence rail: shared .ss-av circle plus the overlap ring. The name on hover is
       the shared [data-tip] card (assets/components/tooltip.ts), placed below because this rail
       lives in the header and there's nothing above it to hang a label in. */
    .pres-av{border:2px solid var(--ss-border-soft);margin-left:-6px;cursor:default;position:relative;transition:transform .15s;animation:avatarIn .3s;overflow:visible;text-decoration:none}
    a.pres-av{cursor:pointer}
    .pres-av img{border-radius:50%}
    .pres-av:first-child{margin-left:0}
    .pres-av:hover{transform:scale(1.15);z-index:1}
    .pres-more{background:var(--ss-surface-soft)!important;color:var(--ss-text-faint)}
    @keyframes avatarIn{from{opacity:0;transform:scale(0)}to{opacity:1;transform:scale(1)}}
    .cbtn{background:none;border:1px solid var(--ss-border-strong);color:var(--ss-text-subtle);border-radius:6px;padding:4px 10px;font-size:.8rem;cursor:pointer;display:flex;align-items:center;gap:5px;transition:border-color .15s,color .15s}
    .cbtn:hover{border-color:var(--ss-border-strong);color:var(--ss-text)}
    /* Pressed = the rail is open and the document is armed for commenting. Without
       it, "why does clicking do something now?" has no answer on screen. */
    .cbtn.active{border-color:var(--ss-brand);color:var(--ss-brand-text);background:var(--ss-brand-tint)}
    /* Two numbers, deliberately two different objects.
       .cmt-n is the TOTAL — how many threads this page holds. It sits in the
       button's row exactly like .like-n and .fav-n on the two controls beside it,
       because it is part of what the button is, and it is permanent.
       .ss-count-badge is UNREAD, and only unread: the corner badge is the bell's
       and Messages' object, and its grammar is "something is waiting for you",
       which means it MUST be able to reach zero. It used to render the total when
       nothing was new — a permanent red-adjacent 3 that invited a click and then
       could not be cleared, because there had never been anything to clear.
       position:relative is what the badge hangs off. */
    #commentToggle{position:relative;padding:5px 7px;line-height:0}
    #commentToggle .cmt-n{font-size:.72rem;font-weight:600;line-height:1;margin-left:4px;font-variant-numeric:tabular-nums}
    #commentToggle .cmt-n:empty{display:none}
    /* Version history: a dropdown of past deploys, and the banner shown while the
       frame is pinned to one of them. */
    /* Session: which agent session made this document, and the way back into it.
       Owner + Pro. The popover is fixed to the viewport, not to the button, because
       on a phone the button lives in the ⋯ menu. */
    .sess-btn { display: inline-flex; align-items: center; gap: 0.35rem; }
    .sess-btn .sess-label { font-size: 0.8rem; font-weight: 600; }
    .sess-pop { display: none; position: fixed; top: 58px; right: 12px; width: min(380px, calc(100vw - 24px)); max-height: min(70vh, 520px); overflow-y: auto; z-index: 70;
      background: var(--ss-surface-raised); border: 1px solid var(--ss-border); border-radius: 12px; box-shadow: var(--ss-elev-pop); padding: 0.4rem; }
    .sess-pop.open { display: block; }
    .sess-pop .sp-head { font-size: 0.68rem; letter-spacing: 0.07em; text-transform: uppercase; font-weight: 700; color: var(--ss-text-faint); padding: 0.45rem 0.6rem 0.3rem; }
    .sess-pop .sp-item { padding: 0.55rem 0.6rem; border-radius: 8px; }
    .sess-pop .sp-item + .sp-item { border-top: 1px solid var(--ss-border-soft); }
    .sess-pop .sp-t { font-weight: 600; font-size: 0.86rem; color: var(--ss-text); }
    .sess-pop .sp-m { font-size: 0.76rem; color: var(--ss-text-subtle); margin-top: 0.15rem; overflow-wrap: anywhere; }
    .sess-pop .sp-acts { display: flex; gap: 0.35rem; flex-wrap: wrap; margin-top: 0.45rem; }
    .sess-pop .sp-act { font: inherit; font-size: 0.74rem; font-weight: 600; color: var(--ss-text-muted); background: none; border: 1px solid var(--ss-border); border-radius: 7px; padding: 0.25rem 0.55rem; cursor: pointer; text-decoration: none; min-height: 30px; display: inline-flex; align-items: center; }
    .sess-pop .sp-act:hover { color: var(--ss-text); border-color: var(--ss-border-strong); }
    .sess-pop .sp-talk { color: var(--ss-brand-text); border-color: var(--ss-brand-tint-border); }
    .sess-pop .sp-empty { font-size: 0.82rem; color: var(--ss-text-subtle); padding: 0.6rem; line-height: 1.5; }
    @media (max-width: 640px) { .sess-btn .sess-label { display: none; } }
    /* A resume command is typed into a terminal; a touch screen has none to paste into. */
    @media (hover: none) and (pointer: coarse) { .sess-pop .sp-copy, .ver-src .ver-copy { display: none; } }
    /* The button leaves for the ⋯ menu and both of its panels are fixed, so the
       wrapper that stays beside the title is no box at all — no empty slot, no gap. */
    .ver-wrap{display:contents}
    /* The rating pill: heart | bookmark inside one border. The segments drop their own
       borders and corners; the bookmark's left edge is the divider. */
    .rate-wrap{display:flex;align-items:stretch;overflow:hidden;border:1px solid var(--ss-border-strong);border-radius:6px;flex-shrink:0}
    .rate-wrap>.cbtn{border:none;border-radius:0}
    .rate-wrap>.bm-btn{border-left:1px solid var(--ss-border-strong)}
    .bm-btn{color:var(--ss-text-subtle);padding:5px 7px;line-height:0}
    /* The tally sits inside the bookmark button, in the bookmark's own colour: one
       control that reads "bookmarked, and this many people keep it". Tabular figures
       so the button doesn't twitch between 9 and 10. Empty (0 or 1) it collapses
       away — your own bookmark needs no counter. */
    .bm-btn .fav-n{font-size:.72rem;font-weight:600;line-height:1;margin-left:4px;font-variant-numeric:tabular-nums}
    .bm-btn .fav-n:empty{display:none}
    .bm-btn:hover{color:var(--ss-crown);background:var(--ss-warn-tint)}
    .bm-btn.on{color:var(--ss-crown);background:var(--ss-warn-tint)}
    /* The heart, left of the bookmark: same shape, its own colour, so "liked" and
       "bookmarked" never read as one state. Named heart-btn, not like-btn, for the
       same reason the bookmark is bm-btn and not fav-btn: this page also carries the
       card CSS (for the share menu's preview), whose .like-btn is hover-revealed. */
    .heart-btn{color:var(--ss-text-subtle);padding:5px 7px;line-height:0}
    .heart-btn .like-n{font-size:.72rem;font-weight:600;line-height:1;margin-left:4px;font-variant-numeric:tabular-nums}
    .heart-btn .like-n:empty{display:none}
    .heart-btn:hover{color:var(--ss-danger-text);background:var(--ss-danger-tint)}
    .heart-btn.on{color:var(--ss-danger-text);background:var(--ss-danger-tint)}
    .ver-btn.pinned{border-color:var(--ss-crown);color:var(--ss-warn-text);background:var(--ss-warn-tint)}
    /* The clock already says "history". The label only speaks when a version is
       pinned — then it reads "v3 · name" — and collapses the rest of the time so the
       button sits in the bar as an icon, like the ones beside it. */
    .ver-btn .ver-label{max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .ver-btn .ver-label:empty{display:none}
    .ver-ov{position:fixed;inset:0;z-index:40;display:none}
    .ver-ov.open{display:block}
    /* Its button lives in the ⋯ menu, so the panel can't hang off it: it opens under
       the header on the right, where the menu was, like the Session panel. */
    .ver-dd{position:fixed;top:58px;right:12px;width:300px;max-width:calc(100vw - 24px);max-height:min(60vh,420px);overflow-y:auto;overscroll-behavior:contain;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.13);padding:.3rem;display:none;z-index:50}
    .ver-dd.open{display:block}
    .ver-dd::-webkit-scrollbar{width:4px}
    .ver-dd::-webkit-scrollbar-thumb{background:var(--ss-border-strong);border-radius:2px}
    .ver-head{font-size:.66rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ss-text-faint);padding:.4rem .55rem .3rem}
    .ver-item{display:block;width:100%;text-align:left;background:none;border:none;border-radius:7px;padding:.42rem .55rem;cursor:pointer;font:inherit;color:var(--ss-text-muted)}
    .ver-name{font-size:.82rem;font-weight:500;display:flex;align-items:center;gap:.35rem}
    .ver-name span.t{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .ver-tag{font-size:.6rem;font-weight:700;letter-spacing:.05em;padding:1px 5px;border-radius:4px;background:rgba(90,90,230,.12);color:var(--ss-brand-text);flex-shrink:0}
    .ver-meta{font-size:.7rem;color:var(--ss-text-faint);margin-top:1px;display:flex;align-items:center;gap:.35rem}
    
    .ver-av{flex-shrink:0;cursor:default}
    
    .ver-src{display:flex;align-items:center;flex-wrap:wrap;gap:.1rem .6rem;font-size:.7rem;line-height:1.35;color:var(--ss-text-faint);padding:0 .55rem .42rem calc(.55rem + 22px + .35rem);margin-top:-.3rem;min-width:0}
    .ver-src .who{flex:1 1 9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .ver-src .who b{font-weight:500;color:var(--ss-text-muted)}
    .ver-src .acts{display:flex;gap:.6rem;flex-shrink:0;margin-left:auto}
    .ver-src a,.ver-src button{font:inherit;font-size:.7rem;font-weight:500;color:var(--ss-brand-text);background:none;border:none;padding:0;cursor:pointer;text-decoration:none;white-space:nowrap}
    .ver-src a:hover,.ver-src button:hover{text-decoration:underline}
    .ver-when{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .ver-empty{padding:.75rem .55rem;font-size:.8rem;color:var(--ss-text-faint);text-align:center}
    /* The row, not its button, carries the ground, so the source line under the button
       is part of the selected row. "Changes" sits over the button's bottom-right
       corner (.ver-main) — a link can't live inside the button. */
    .ver-row{border-radius:7px}
    .ver-row:hover{background:var(--ss-surface-muted)}
    .ver-row.on{background:var(--ss-brand-tint)}
    .ver-main{position:relative}
    .ver-cmp{position:absolute;right:.55rem;bottom:.45rem;font-size:.68rem;font-weight:600;color:var(--ss-brand-text);text-decoration:none;padding:1px 6px;border-radius:5px;background:var(--ss-surface);border:1px solid var(--ss-border);opacity:0;transition:opacity .12s}
    .ver-row:hover .ver-cmp,.ver-cmp:focus-visible{opacity:1}
    @media (hover:none){.ver-cmp{opacity:1}}
    .ver-cmp:hover{border-color:var(--ss-brand-tint-border);background:var(--ss-brand-tint)}
    .ver-head{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
    .ver-head a{font-size:.66rem;font-weight:600;letter-spacing:0;text-transform:none;color:var(--ss-brand-text);text-decoration:none}
    .ver-head a:hover{text-decoration:underline}
    .ver-banner a.ver-diff{background:var(--ss-surface);border:1px solid var(--ss-warn-tint-border);color:var(--ss-warn-text);border-radius:6px;padding:3px 10px;font-size:.78rem;text-decoration:none;white-space:nowrap}
    .ver-banner a.ver-diff:hover{border-color:var(--ss-warn-text)}
    .ver-banner{display:flex;align-items:center;gap:.6rem;padding:.5rem 1.5rem;background:var(--ss-warn-tint);border-bottom:1px solid var(--ss-warn-tint-border);font-size:.82rem;color:var(--ss-warn-text);flex-shrink:0}
    .ver-banner b{font-weight:600}
    .ver-banner .sp{margin-left:auto;display:flex;gap:.4rem;flex-shrink:0}
    .ver-banner button{background:var(--ss-surface);border:1px solid var(--ss-warn-tint-border);color:var(--ss-warn-text);border-radius:6px;padding:3px 10px;font-size:.78rem;cursor:pointer;font-family:inherit;white-space:nowrap}
    .ver-banner button:hover{border-color:var(--ss-warn-tint-border)}
    .ver-banner button.primary{background:var(--ss-brand);border-color:var(--ss-brand);color:var(--ss-inverse-text)}
    .ver-banner button.primary:hover{background:var(--ss-brand-hover)}
    .ver-banner button:disabled{opacity:.55;cursor:default}
    /* Edit mode (⋯ → Edit): the bar that says the page is open for editing, and the
       two ways out of it. Brand, not warn: it is a mode the reader chose, not a
       caution about what they are looking at. An error turns it danger until the
       next thing it says. */
    .edit-bar{display:flex;align-items:center;gap:.6rem;padding:.45rem 1.5rem;background:var(--ss-brand-tint);border-bottom:1px solid var(--ss-brand-tint-border);font-size:.82rem;color:var(--ss-brand-text);flex-shrink:0}
    .edit-bar[hidden]{display:none}
    .edit-bar .eb-ic{display:inline-flex;flex-shrink:0}
    .edit-bar .eb-text{min-width:0;line-height:1.35}
    .edit-bar.is-err{background:var(--ss-danger-tint);border-bottom-color:var(--ss-danger-tint-border);color:var(--ss-danger-text)}
    .edit-bar.is-ok{background:var(--ss-success-tint);border-bottom-color:var(--ss-success-tint-border);color:var(--ss-success-text)}
    .edit-bar .sp{margin-left:auto;display:flex;gap:.4rem;flex-shrink:0}
    .edit-bar button{background:var(--ss-surface);border:1px solid var(--ss-brand-tint-border);color:var(--ss-text-muted);border-radius:6px;padding:4px 12px;font-size:.8rem;font-weight:600;cursor:pointer;font-family:inherit;white-space:nowrap}
    .edit-bar button:hover{border-color:var(--ss-border-strong);color:var(--ss-text)}
    .edit-bar button.primary{background:var(--ss-brand);border-color:var(--ss-brand);color:var(--ss-on-brand)}
    .edit-bar button.primary:hover{background:var(--ss-brand-hover);border-color:var(--ss-brand-hover);color:var(--ss-on-brand)}
    .edit-bar button:disabled{opacity:.5;cursor:default}
    .edit-bar button.primary:disabled:hover{background:var(--ss-brand);border-color:var(--ss-brand)}
    .edit-bar button.danger{background:var(--ss-danger-tint);border-color:var(--ss-danger-tint-border);color:var(--ss-danger-text)}
    .edit-bar button.danger:hover{border-color:var(--ss-danger-text);color:var(--ss-danger-text)}
    .edit-bar button:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .edit-btn[aria-disabled="true"]{opacity:.5;cursor:default}
    /* Document tools footer: a thin full-width strip under the viewer, for whoever
       the doc-tools flag lets in (doc-tools-flag.ts). */
    .doc-tools{flex-shrink:0;height:32px;display:flex;align-items:center;gap:.5rem;padding:0 1rem;border-top:1px solid var(--ss-border);background:var(--ss-bg-chrome);color:var(--ss-text-faint);font-size:.74rem}
    .doc-tools b{font-weight:600;color:var(--ss-text-subtle);margin-right:.25rem}
    .dt-btn{display:inline-flex;align-items:center;gap:6px;height:22px;white-space:nowrap;padding:0 9px;border:1px solid var(--ss-border);border-radius:6px;background:var(--ss-surface);color:var(--ss-text-subtle);font:inherit;font-size:.74rem;cursor:pointer}
    .dt-btn:hover{border-color:var(--ss-border-strong);color:var(--ss-text)}
    .dt-btn:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .dt-btn.on{background:var(--ss-brand-tint);border-color:var(--ss-brand-tint-border);color:var(--ss-brand-text)}
    .dt-btn:disabled{opacity:.5;cursor:default}
    /* The swatch is the ink itself (shared/highlight.ts MARK_STYLE), not chrome:
       it stays marker yellow in both themes on purpose. */
    .dt-swatch{width:12px;height:8px;border-radius:3px 2px;background:#ffd60a}
    /* Spotlight menu (the kebab beside Highlight): six veil colours for the page,
       opening upward since the footer is the bottom edge. The swatches are the
       veils themselves (DIM_COLORS), like the marker swatch above. */
    .dt-more{position:relative;display:inline-flex}
    .dt-kebab{width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;padding:0;border:1px solid var(--ss-border);border-radius:6px;background:var(--ss-surface);color:var(--ss-text-subtle);cursor:pointer}
    .dt-kebab svg{transform:rotate(90deg)}
    .dt-kebab:hover,.dt-kebab[aria-expanded="true"]{border-color:var(--ss-border-strong);color:var(--ss-text)}
    .dt-kebab:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .dt-kebab.on{background:var(--ss-brand-tint);border-color:var(--ss-brand-tint-border);color:var(--ss-brand-text)}
    /* A split button: the tool and its kebab share one outline, no gap. */
    .dt-split{display:inline-flex}
    .dt-split>.dt-btn{border-radius:6px 0 0 6px}
    .dt-split .dt-kebab{border-radius:0 6px 6px 0;margin-left:-1px}
    .dt-split .dt-btn:hover,.dt-split .dt-kebab:hover,.dt-split .dt-kebab[aria-expanded="true"],.dt-split .on,.dt-split :focus-visible{position:relative;z-index:1}
    /* Reader view's two states, as plain radio rows. */
    .dt-opts{display:flex;flex-direction:column;gap:2px;min-width:180px}
    .dt-opt{display:flex;align-items:center;gap:8px;width:100%;padding:5px 8px;border:none;border-radius:6px;background:none;font:inherit;font-size:.78rem;color:var(--ss-text);text-align:left;cursor:pointer}
    .dt-opt:hover{background:var(--ss-surface-soft-hover)}
    .dt-opt:focus-visible{outline:2px solid var(--ss-focus);outline-offset:-2px}
    .dt-opt .dt-tick{width:14px;display:inline-flex;color:var(--ss-brand-text);visibility:hidden}
    .dt-opt[aria-checked="true"] .dt-tick{visibility:visible}
    .dt-opt small{margin-left:auto;padding-left:12px;color:var(--ss-text-faint);font-size:.7rem}
    .dt-menu{position:absolute;bottom:calc(100% + 8px);left:0;z-index:70;width:max-content;padding:10px 12px 8px;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:10px;box-shadow:var(--ss-elev-pop);color:var(--ss-text)}
    .dt-menu[hidden]{display:none}
    .dt-menu-h{font-size:.74rem;font-weight:600;margin-bottom:9px}
    .dt-menu-sec+.dt-menu-sec{margin-top:10px;padding-top:10px;border-top:1px solid var(--ss-border-soft)}
    .dt-menu-note{font-size:.7rem;color:var(--ss-text-faint);margin-top:7px}
    .dt-sw:disabled{opacity:.4;cursor:default}
    .dt-sws{display:flex;align-items:center;gap:9px}
    .dt-sw{width:26px;height:26px;padding:0;border-radius:50%;border:2px solid var(--ss-surface);box-shadow:0 0 0 1px var(--ss-border);cursor:pointer}
    .dt-sw:hover{box-shadow:0 0 0 1px var(--ss-border-strong)}
    .dt-sw[aria-checked="true"]{box-shadow:0 0 0 2px var(--ss-brand)}
    .dt-sw:focus-visible{outline:2px solid var(--ss-focus);outline-offset:3px}
    .dt-dim-toggle{display:flex;align-items:center;gap:10px;width:250px;min-height:44px;margin-bottom:9px;font-size:.74rem;font-weight:600;cursor:pointer}
    .dt-dim-toggle>span:first-child{flex:1}
    .dt-dim-toggle input{appearance:none;flex:none;width:36px;height:22px;margin:0;border:1px solid var(--ss-border-strong);border-radius:12px;background:var(--ss-text-faint);cursor:pointer}
    .dt-dim-toggle input::before{content:"";display:block;width:16px;height:16px;margin:2px;border-radius:50%;background:var(--ss-surface)}
    .dt-dim-toggle input:checked{background:var(--ss-brand);border-color:var(--ss-brand)}
    .dt-dim-toggle input:checked::before{transform:translateX(14px)}
    .dt-dim-toggle input:focus-visible{outline:2px solid var(--ss-focus);outline-offset:3px}
    .dt-dim-state{min-width:2em;color:var(--ss-text-subtle)}
    /* A phone keeps the strip to its tools: no crown or label, and no highlighter,
       which needs a precise text selection a touch screen does not give. */
    @media(max-width:640px){.doc-tools>b,.doc-tools .dt-hl{display:none}}
    @media print{.doc-tools{display:none}}
    .sidebar{width:var(--sb-w,380px);flex-shrink:0;position:relative;background:var(--ss-surface);border-left:1px solid var(--ss-border);display:flex;flex-direction:column;transition:width .2s}
    .sidebar.hidden{width:0;min-width:0;border:none;overflow:hidden}
    /* Drag handles on the inner edge of both panels. The widths are the --chat-w /
       --sb-w properties on :root (set and remembered by the resize script), so the
       open/close transition and the chat's fixed-width inner both follow them.
       While a drag runs the frame takes no pointer events — otherwise the cursor
       crossing into the document is lost to it and the drag sticks — and the width
       transition is off so the panel tracks the cursor instead of trailing it. */
    .rz{position:absolute;top:0;bottom:0;width:9px;cursor:col-resize;z-index:5;touch-action:none;outline:none}
    .rz::after{content:"";position:absolute;top:0;bottom:0;left:4px;width:2px;background:transparent;transition:background .15s}
    .rz:hover::after,.rz.on::after,.rz:focus-visible::after{background:var(--ss-brand)}
    .chat-panel .rz{right:0}
    .chat-panel .rz::after{left:auto;right:0}
    .chat:not(.open) .rz,.sidebar.hidden .rz{display:none}
    .sidebar .rz{left:-5px}
    body.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}
    body.resizing iframe{pointer-events:none}
    body.resizing .chat-panel,body.resizing .sidebar{transition:none}
    .sb-hdr{padding:.75rem 1rem;border-bottom:1px solid var(--ss-border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
    .sb-hdr h3{font-size:.9rem;font-weight:600}
    /* Twice the glyph, same header: the negative margins keep the taller line
       box from setting the header's height. */
    .sb-close{background:none;border:none;color:var(--ss-text-subtle);cursor:pointer;font-size:1.8rem;padding:0 6px;line-height:1;margin:-.1rem 0}
    @media(hover:hover){.sb-close:hover{color:var(--ss-text)}}
    /* Preferences bar: a quiet strip under the header, owner-only. It holds the
       switches that shape what everyone else sees on this space — so it sits with
       the comments it governs, not in a settings page two clicks away. */
    .sb-prefs{flex-shrink:0;border-bottom:1px solid var(--ss-border);padding:.3rem .6rem;position:relative}
    .pref-pop>summary{list-style:none;display:inline-flex;align-items:center;gap:5px;font-size:.72rem;color:var(--ss-text-subtle);padding:3px 7px;border-radius:6px;cursor:pointer}
    .pref-pop>summary::-webkit-details-marker{display:none}
    .pref-pop>summary:hover{background:var(--ss-surface-muted);color:var(--ss-text)}
    .pref-pop[open]>summary{background:var(--ss-surface-muted);color:var(--ss-text)}
    .pref-pop>summary:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .pref-pop svg{display:block}
    .pref-menu{position:absolute;top:calc(100% - 2px);left:.6rem;right:.6rem;z-index:20;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.12);padding:.5rem}
    /* The switch reads left to right: what it does, then its state. */
    .pref-row{display:flex;align-items:flex-start;gap:.55rem;padding:.35rem .4rem;border-radius:8px;cursor:pointer}
    .pref-row:hover{background:var(--ss-bg)}
    .pref-row input{margin:2px 0 0;accent-color:var(--ss-brand-text);cursor:pointer;flex-shrink:0}
    .pref-row b{display:block;font-size:.78rem;font-weight:500;color:var(--ss-text)}
    .pref-row i{display:block;font-style:normal;font-size:.7rem;color:var(--ss-text-subtle);line-height:1.4;margin-top:1px}
    .pref-row.busy{opacity:.55;pointer-events:none}
    .sb-list{flex:1;overflow-y:auto;padding:.5rem}

    .sb-tabs{display:flex;gap:4px;padding:.4rem .6rem;border-bottom:1px solid var(--ss-border);flex-shrink:0}
    .sb-tabs button{flex:1;font:inherit;font-size:.78rem;padding:.38rem .5rem;border-radius:6px;border:1px solid transparent;background:none;color:var(--ss-text-subtle);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:5px}
    .sb-tabs button[aria-selected="true"]{background:var(--ss-surface);border-color:var(--ss-border);color:var(--ss-text);font-weight:600}
    .sb-tabs button:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .sb-tabs .dn:empty{display:none}
    .sb-tabs .dn{font-size:.66rem;font-weight:700;background:var(--ss-brand);color:var(--ss-on-brand);border-radius:999px;padding:0 6px;line-height:1.5}
    .sidebar.disc-on .sb-prefs,.sidebar.disc-on .sb-list,.sidebar.disc-on .sb-compose,.sidebar.disc-on #annotSw{display:none}
    .sidebar.disc-on #sbTitle>span{display:none}
    .sidebar.disc-on #sbTitle::after{content:"Discussion"}
    .disc{flex:1;display:flex;flex-direction:column;min-height:0}
    .disc[hidden]{display:none}
    .disc-who{display:flex;gap:6px;align-items:baseline;padding:.4rem .9rem;font-size:.72rem;color:var(--ss-text-muted);border-bottom:1px solid var(--ss-border-soft);flex-shrink:0;white-space:nowrap;overflow:hidden}
    .disc-who[hidden]{display:none}
    .disc-who .dw-l{color:var(--ss-success-text);font-weight:600;flex-shrink:0}
    .disc-who .dw-l::before{content:"";display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--ss-success);margin-right:5px;vertical-align:1px}
    .disc-who .dw{overflow:hidden;text-overflow:ellipsis}
    .disc-list{flex:1;overflow-y:auto;padding:.25rem .9rem .5rem}
    .disc-empty{padding:1.5rem .2rem;color:var(--ss-text-subtle);font-size:.82rem;line-height:1.5}
    .disc-empty b{display:block;color:var(--ss-text);font-size:.88rem;margin-bottom:4px}
    .disc-empty p{margin:0}
    .dt{padding:.75rem 0 .55rem;border-bottom:1px solid var(--ss-border-soft)}
    .dt:last-child{border-bottom:none}
    .dt-h{display:flex;flex-wrap:wrap;gap:6px;align-items:baseline;margin-bottom:4px}
    .dt-title{font-size:.9rem;line-height:1.35;font-weight:650;color:var(--ss-text);overflow-wrap:anywhere}
    .dt-tag{font-size:.64rem;color:var(--ss-text-subtle);background:var(--ss-surface-muted);border-radius:4px;padding:1px 6px}
    .dt-tag.on{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .dt.pinned .dt-tag:first-child{color:var(--ss-brand-text);background:var(--ss-brand-tint)}
    .dt-dec{font-size:.8rem;line-height:1.45;color:var(--ss-text)}
    .dt-dec b{color:var(--ss-success-text);font-weight:600}
    .dt.decided .dt-title{color:var(--ss-text-muted)}
    .dp-wrap.sub{border-left:2px solid var(--ss-border-soft);padding-left:10px;margin-top:2px}
    .dp{padding:3px 0}
    .dp-h{display:flex;align-items:baseline;gap:6px;font-size:.72rem;line-height:1.4}
    .dp-who{font-weight:600;color:var(--ss-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
    .dp-who.agent{color:var(--ss-brand-text)}
    .dp-meta{color:var(--ss-text-subtle);flex-shrink:0}
    .dp-num{margin-left:auto;font:.64rem ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--ss-text-faint);flex-shrink:0}
    .dp-to{font-size:.66rem;color:var(--ss-text-subtle)}
    .dp-b{font-size:.84rem;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;color:var(--ss-text);margin-top:1px}
    .dp-res{font-size:.74rem;color:var(--ss-success-text);margin-top:2px}
    .dp-acts{display:flex;gap:12px;margin-top:2px}
    .dl{font:inherit;font-size:.7rem;background:none;border:none;padding:4px 0;color:var(--ss-text-subtle);cursor:pointer;min-height:24px}
    @media(hover:hover){.dl:hover{color:var(--ss-brand-text)}
      .dp .dl.more{opacity:0;transition:opacity .12s}
      .dp:hover>.dp-acts .dl.more,.dp:focus-within>.dp-acts .dl.more{opacity:1}}
    .dl:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px;border-radius:3px}
    .dp-fold>summary{font-size:.7rem;color:var(--ss-text-subtle);cursor:pointer;padding:3px 0 3px 12px}
    .dr{display:flex;gap:6px;align-items:flex-end;margin:4px 0 6px 10px}
    .dr textarea,.dr input{flex:1;min-width:0;font:inherit;font-size:16px;line-height:1.4;border:1px solid var(--ss-border);border-radius:10px;padding:6px 10px;background:var(--ss-field);color:var(--ss-text);resize:none}
    @media(min-width:641px){.dr textarea,.dr input{font-size:.82rem}}
    .dr textarea:focus-visible,.dr input:focus-visible{outline:2px solid var(--ss-focus);outline-offset:0}
    .disc-compose{border-top:1px solid var(--ss-border);padding:.55rem .7rem .45rem;display:flex;flex-direction:column;gap:5px;flex-shrink:0}
    .disc-compose input{font:inherit;font-size:16px;border:1px solid var(--ss-border);border-radius:10px;padding:6px 10px;background:var(--ss-field);color:var(--ss-text);width:100%;font-weight:600}
    .disc-compose input[hidden]{display:none}
    .disc-box{display:flex;gap:6px;align-items:flex-end;border:1px solid var(--ss-border);border-radius:12px;background:var(--ss-field);padding:4px 4px 4px 10px}
    .disc-box:focus-within{border-color:var(--ss-focus)}
    .disc-box textarea{flex:1;min-width:0;font:inherit;font-size:16px;line-height:1.45;border:none;background:none;color:var(--ss-text);resize:none;padding:4px 0;outline:none;max-height:160px}
    @media(min-width:641px){.disc-box textarea,.disc-compose input{font-size:.84rem}}
    .disc-send{flex-shrink:0;width:32px;height:32px;border-radius:50%;border:none;background:var(--ss-brand);color:var(--ss-on-brand);font-size:1rem;font-weight:700;cursor:pointer;display:grid;place-items:center;line-height:1}
    .disc-send.sm{width:28px;height:28px;font-size:.9rem}
    .disc-send:disabled{opacity:.5;cursor:default}
    .disc-send:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .disc-row{display:flex;align-items:center;gap:8px;padding:0 2px}
    .disc-link{font:inherit;font-size:.7rem;background:none;border:none;padding:2px 0;color:var(--ss-brand-text);cursor:pointer}
    .disc-link[hidden]{display:none}
    .disc-hint{margin-left:auto;font-size:.66rem;color:var(--ss-text-subtle);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .disc-err{font-size:.72rem;color:var(--ss-danger-text)}
    .disc-err[hidden]{display:none}
    .sb-list::-webkit-scrollbar{width:4px}
    .sb-list::-webkit-scrollbar-thumb{background:var(--ss-border-strong);border-radius:2px}
    .thread{margin-bottom:.6rem;border:1px solid var(--ss-border);border-radius:8px}
    /* A resolved thread reads quieter; a confirm or an edit opened inside it doesn't. */
    .thread.resolved .cmt>:not(.cmt-confirm):not(.cmt-edit),.thread.resolved>.resolve-bar{opacity:.6}
    .thread.flash{animation:flashC 1s ease}
    @keyframes flashC{0%,100%{border-color:var(--ss-border)}50%{border-color:var(--ss-brand)}}
    .cmt{padding:.55rem .7rem .45rem;position:relative}
    .cmt+.cmt{border-top:1px solid var(--ss-border-soft)}
    /* A reply reads as a reply: tucked under its parent on a thread line, not a
       second root at full weight. */
    .cmt.reply{margin-left:1.1rem;padding-left:.8rem;border-left:2px solid var(--ss-brand-tint-border);border-top:none;padding-top:.35rem;padding-bottom:.45rem}
    .cmt.reply .cmt-avatar{transform:scale(.85)}
    /* Unread since you last looked at this rail (localStorage, per space). */
    .cmt.new{background:var(--ss-brand-tint)}
    .cmt.new.reply{border-left-color:var(--ss-brand)}
    .new-pill{font-size:.58rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;background:var(--ss-brand);color:var(--ss-inverse-text);border-radius:4px;padding:1px 5px;margin-left:auto}
    .new-pill+.cmt-time{margin-left:6px}
    .more-replies{display:block;width:100%;text-align:left;background:none;border:none;border-top:1px solid var(--ss-border-soft);color:var(--ss-brand-text);font-size:.74rem;padding:.4rem .75rem .4rem 2rem;cursor:pointer;font-family:inherit}
    .more-replies:hover{background:var(--ss-bg)}
    /* The always-visible way into a thread: the one button every card shows. The
       rest of a card's actions wait in its toolbar (.cmt-tools). */
    .thread-foot{display:flex;align-items:center;gap:6px;padding:0 .7rem .4rem;margin-top:-2px}
    .thread-reply{display:inline-flex;align-items:center;gap:5px;background:none;border:1px solid transparent;color:var(--ss-brand-text);font-size:.74rem;font-weight:500;padding:2px 8px;margin-left:-8px;border-radius:999px;cursor:pointer;font-family:inherit;transition:background .15s,border-color .15s}
    @media(hover:hover){.thread-reply:hover{background:var(--ss-brand-tint);border-color:var(--ss-brand-tint-border)}}
    .thread-foot .thread-people{font-size:.7rem;color:var(--ss-text-subtle);margin-left:auto}
    /* Inline reply box: its own textarea under the thread, so the main composer at the
       foot of the rail never moves or loses what was typed in it. */
    .inline-reply{border-top:1px dashed var(--ss-brand-tint-border);background:var(--ss-brand-tint);border-radius:0 0 8px 8px;padding:.5rem .6rem}
    .inline-reply .compose-row textarea{min-height:34px}
    .inline-reply .inline-cancel{background:none;border:none;color:var(--ss-text-subtle);font-size:.72rem;cursor:pointer;padding:4px 2px 0;font-family:inherit}
    @media(hover:hover){.inline-reply .inline-cancel:hover{color:var(--ss-text)}}
    .inline-reply .inline-hint{font-size:.7rem;color:var(--ss-text-subtle);margin-left:8px}
    .cmt-edit{margin:.25rem 0 .35rem}
    .cmt-edit .compose-row textarea{min-height:34px}
    .cmt-edit-row{display:flex;align-items:center;gap:8px;margin-top:6px;flex-wrap:wrap}
    .cmt-edit .inline-cancel{background:none;border:none;color:var(--ss-text-subtle);font-size:.72rem;cursor:pointer;padding:4px 2px;font-family:inherit}
    @media(hover:hover){.cmt-edit .inline-cancel:hover{color:var(--ss-text)}}
    .cmt-edit .inline-hint{font-size:.7rem;color:var(--ss-text-subtle)}
    .cmt-edited{color:var(--ss-text-faint);font-size:.7rem;white-space:nowrap}
    .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
    /* Keyboard users get the same affordances as mouse users: the action row lights
       up on focus, and every control shows a visible ring. */
    .thread:focus-within .cmt-actions{opacity:1}
    .cmt-tool:focus-visible,.cmt-unfold:focus-visible,.cmt-act:focus-visible,.thread-reply:focus-visible,.more-replies:focus-visible,.rxn:focus-visible,.cmt-anchor:focus-visible,.sb-close:focus-visible,.pin-btn:focus-visible,.inline-cancel:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .rxn{font-family:inherit;color:inherit}
    .cmt-anchor{border:none;font-family:inherit}
    .sb-empty{padding:2.25rem 1.25rem 1.5rem;text-align:center;color:var(--ss-text-subtle);font-size:.84rem;line-height:1.5}
    .sb-empty .big{font-size:1.9rem;margin-bottom:.4rem}
    .sb-empty b{color:var(--ss-text-muted)}
    .sb-empty .hint{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:.9rem;font-size:.76rem;color:var(--ss-text-subtle)}
    .sb-empty .hint-p{margin-top:.8rem;font-size:.76rem;line-height:1.5;color:var(--ss-text-subtle)}
    .sb-empty .hint-p b{color:var(--ss-text-muted)}
    .sb-empty .fake-pill{display:inline-flex;align-items:center;gap:2px;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:999px;padding:1px 7px;box-shadow:0 2px 8px rgba(0,0,0,.08);font-size:.78rem}
    .sb-empty .starter{margin-top:1rem;background:var(--ss-brand);color:var(--ss-inverse-text);border:none;border-radius:999px;padding:6px 14px;font-size:.78rem;font-weight:500;cursor:pointer;font-family:inherit}
    @media(hover:hover){.sb-empty .starter:hover{background:var(--ss-brand-hover)}}
    
    .cbtn-txt{font-size:.78rem;line-height:1}
    .cmt-head{display:flex;align-items:center;gap:6px;min-height:24px;margin-bottom:2px}
    .is-bot-avatar{background:linear-gradient(135deg,var(--ss-brand),var(--ss-brand))}
    .cmt-profile{color:inherit;text-decoration:none}
    .cmt-profile:hover{text-decoration:underline}
    .cmt-profile:focus-visible{outline:2px solid var(--ss-brand);outline-offset:3px;border-radius:3px}
    .cmt-avatar-link{display:inline-flex;flex-shrink:0}
    .cmt-author{font-size:.8rem;font-weight:600;color:var(--ss-text)}
    .bot-badge{font-size:.58rem;font-weight:700;letter-spacing:.05em;padding:1px 5px;border-radius:4px;background:rgba(90,90,230,.12);color:var(--ss-brand-text);cursor:default}
    .cmt-time{font-size:.7rem;color:var(--ss-text-subtle);margin-left:auto}
    /* The words are what a card is for: full-strength text, the name beside them
       at the same weight, the time and the tools quieter. */
    .cmt-body{font-size:.84rem;line-height:1.5;color:var(--ss-text);word-break:break-word}
    /* A long comment folds to its first eight lines (measured in measureFolds). */
    .cmt-body.clamp{max-height:12em;overflow:hidden}
    .cmt-body.clamp.is-cut{-webkit-mask-image:linear-gradient(to bottom,black 65%,transparent);mask-image:linear-gradient(to bottom,black 65%,transparent)}
    .cmt-unfold{display:none;background:none;border:none;padding:2px 0;margin-top:2px;font:inherit;font-size:.74rem;font-weight:600;color:var(--ss-brand-text);cursor:pointer}
    .cmt-body.is-cut+.cmt-unfold{display:inline-block}
    @media(hover:hover){.cmt-unfold:hover{text-decoration:underline;text-underline-offset:2px}}
    /* A card's actions. With a pointer they float over the card's top-right corner
       on hover or focus, so a rail of comments reads as words, not as rows of
       buttons; a touch screen has no hover and keeps only ⋯, whose menu carries
       react and resolve too. */
    .cmt-tools{display:flex;align-items:center;gap:1px;flex:none}
    .cmt-tool{display:inline-flex;align-items:center;justify-content:center;width:26px;height:24px;padding:0;border:none;border-radius:6px;background:none;color:var(--ss-text-subtle);cursor:pointer;font-family:inherit;-webkit-tap-highlight-color:transparent;transition:background .12s,color .12s}
    .cmt-tool svg{display:block}
    .cmt-tool[aria-expanded="true"]{background:var(--ss-surface-soft);color:var(--ss-text)}
    @media(hover:hover){
      .cmt-tool:hover{background:var(--ss-surface-soft);color:var(--ss-text)}
      .cmt-tools{position:absolute;top:.3rem;right:.4rem;padding:2px;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:8px;box-shadow:var(--ss-elev-1);opacity:0;pointer-events:none;transition:opacity .12s}
      .cmt:hover>.cmt-head .cmt-tools,.cmt:focus-within>.cmt-head .cmt-tools,.cmt.tools-on>.cmt-head .cmt-tools{opacity:1;pointer-events:auto}
    }
    @media(hover:none){
      .cmt-tools{margin:-4px -8px -4px 0}
      .cmt-tools .t-hov{display:none}
      .cmt-tool{width:36px;height:32px}
    }
    /* "Whole page", said once above the threads that have no spot. */
    .sb-sec{display:flex;align-items:center;gap:6px;margin:.85rem .2rem .45rem;font-size:.64rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--ss-text-subtle)}
    .sb-sec::after{content:"";flex:1;border-top:1px solid var(--ss-border-soft)}
    .sb-sec-n{font-weight:600;color:var(--ss-text-faint)}
    .cmt-gone{font-size:.8rem;font-style:italic;color:var(--ss-text-faint);padding:.15rem 0}
    /* Markdown in a comment (mkBody): tight like the card, not the chat panel's spacing. */
    .cmt-body>:first-child{margin-top:0}
    .cmt-body>:last-child{margin-bottom:0}
    .cmt-body p{margin:.45em 0}
    .cmt-body ul,.cmt-body ol{margin:.45em 0;padding-left:1.35em}
    .cmt-body li+li{margin-top:.15em}
    .cmt-body .md-h{font-weight:700;color:var(--ss-text);margin:.7em 0 .3em}
    .cmt-body strong{font-weight:650;color:var(--ss-text)}
    .cmt-body code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:.88em;padding:.08em .3em;border-radius:4px;background:var(--ss-surface-muted);color:var(--ss-text)}
    .cmt-body pre{max-width:100%;box-sizing:border-box;overflow-x:auto;white-space:pre;padding:.6em .75em;margin:.5em 0;border:1px solid var(--ss-border-soft);border-radius:7px;background:var(--ss-surface-muted);line-height:1.5}
    .cmt-body pre code{padding:0;background:none}
    .cmt-body a:not(.mention){color:var(--ss-brand-text);text-decoration:underline;text-underline-offset:2px}
    
    
    /* A space link in the words: the space's emoji and title, a quiet pill that still reads as a link. */
    a.ss-lc-chip{display:inline-flex;align-items:center;gap:5px;max-width:100%;vertical-align:bottom;padding:1px 7px 1px 5px;margin:1px 0;border-radius:6px;border:1px solid var(--ss-border);background:var(--ss-surface-muted);color:var(--ss-text)!important;text-decoration:none!important;font-weight:550;line-height:1.5;transition:background .15s,border-color .15s}
    a.ss-lc-chip .ss-lc-ic{flex:none;font-size:.95em;line-height:1}
    a.ss-lc-chip .ss-lc-t{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    a.ss-lc-chip .ss-lc-pg{flex:none;max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ss-text-subtle);font-weight:400}
    a.ss-lc-chip .ss-lc-pg::before{content:"/ ";color:var(--ss-text-faint)}
    @media(hover:hover){a.ss-lc-chip:hover{background:var(--ss-brand-tint);border-color:var(--ss-brand-tint-border)}}
    a.ss-lc:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    a.ss-lc-label{text-decoration-style:dotted!important}
    /* The card on hover: a glance, not a page. */
    .ss-lcp{position:fixed;z-index:80;box-sizing:border-box;border-radius:10px;border:1px solid var(--ss-border);background:var(--ss-surface-raised,var(--ss-surface));color:var(--ss-text);box-shadow:var(--ss-elev-pop);overflow:hidden;font-size:13px;line-height:1.45;text-align:left;opacity:0;transform:translateY(3px);transition:opacity .14s ease,transform .14s ease}
    .ss-lcp.on{opacity:1;transform:none}
    .ss-lcp[hidden]{display:none}
    @media(prefers-reduced-motion:reduce){.ss-lcp{transition:none;transform:none}}
    /* The whole card is the link (a second way to open the page, never a button). */
    .ss-lcp-main{display:block;color:inherit;text-decoration:none}
    .ss-lcp-main:focus-visible{outline:2px solid var(--ss-focus);outline-offset:-2px;border-radius:10px}
    @media(hover:hover){.ss-lcp-main:hover .ss-lcp-title{text-decoration:underline;text-underline-offset:2px}}
    .ss-lcp-hero{aspect-ratio:16/9;background:var(--ss-surface-muted);border-bottom:1px solid var(--ss-border-soft);overflow:hidden}
    .ss-lcp-hero[hidden]{display:none}
    .ss-lcp-hero img{display:block;width:100%;height:100%;object-fit:cover}
    /* A screenshot is the page's first screen: show it from its top. */
    .ss-lcp-hero.is-shot img{object-position:top}
    .ss-lcp-body{padding:10px 12px 12px}
    .ss-lcp-head{display:flex;align-items:center;gap:8px;padding-right:30px}
    .ss-lcp-emoji{flex:none;font-size:18px;line-height:1}
    .ss-lcp-tt{min-width:0;flex:1}
    .ss-lcp-title{font-weight:650;font-size:14px;line-height:1.3;color:var(--ss-text);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
    .ss-lcp-page{margin-top:1px;font-size:12px;color:var(--ss-text-subtle);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .ss-lcp-desc{margin:6px 0 0;color:var(--ss-text-muted);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
    .ss-lcp-by{display:flex;align-items:center;gap:6px;margin-top:8px;font-size:12px;color:var(--ss-text-subtle);min-width:0;white-space:nowrap}
    .ss-lcp-by .ss-av{flex:none;width:18px;height:18px;font-size:9px}
    .ss-lcp-who{color:var(--ss-text);font-weight:550;overflow:hidden;text-overflow:ellipsis}
    .ss-lcp-up{overflow:hidden;text-overflow:ellipsis}
    .ss-lcp-dot{color:var(--ss-text-faint)}
    /* Copy rides the card's corner: over the picture, or beside the title without one. */
    .ss-lcp .ss-lcp-copy{position:absolute;top:8px;right:8px;padding:6px;background:var(--ss-surface-raised,var(--ss-surface));border-color:var(--ss-border);box-shadow:var(--ss-elev-1)}
    .ss-lcp .ss-lcp-copy:hover{background:var(--ss-surface-muted);color:var(--ss-text)}

    /* Pictures in a comment (comment-images.ts): under the words, each opens full size. */
    .cmt-pics{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
    .cmt-pic{display:block;max-width:100%;border-radius:8px;overflow:hidden;border:1px solid var(--ss-border);background:var(--ss-surface-muted);line-height:0}
    .cmt-pics.n2 .cmt-pic,.cmt-pics.n3 .cmt-pic,.cmt-pics.n4 .cmt-pic{max-width:calc(50% - 3px)}
    .cmt-pic img{display:block;max-width:100%;max-height:220px;width:auto;height:auto;object-fit:contain}
    .cmt-tray{display:flex;flex-wrap:wrap;gap:6px;padding:2px 0 6px}
    .cmt-tray[hidden]{display:none}
    .cmt-tray-item{position:relative;width:56px;height:56px;border-radius:8px;overflow:hidden;border:1px solid var(--ss-border);background:var(--ss-surface-muted)}
    .cmt-tray-item img{width:100%;height:100%;object-fit:cover;display:block}
    .cmt-tray-item.up img{opacity:.45}
    .cmt-tray-item.up::after{content:"";position:absolute;inset:0;margin:auto;width:18px;height:18px;border-radius:50%;border:2px solid var(--ss-brand);border-right-color:transparent;animation:cmt-spin .8s linear infinite}
    @keyframes cmt-spin{to{transform:rotate(360deg)}}
    @media (prefers-reduced-motion:reduce){.cmt-tray-item.up::after{animation:none}}
    .cmt-tray-x{position:absolute;top:2px;right:2px;width:20px;height:20px;border-radius:50%;border:0;padding:0;background:var(--ss-inverse-surface);color:var(--ss-inverse-text);font-size:13px;line-height:20px;text-align:center;cursor:pointer}
    .cmt-drop-on{outline:2px dashed var(--ss-brand);outline-offset:2px;border-radius:10px}
    .cmt-anchor{display:inline-flex;align-items:center;gap:3px;background:var(--ss-brand-tint);color:var(--ss-brand-text);font-size:.7rem;padding:1px 6px;border-radius:3px;cursor:pointer;margin-bottom:4px;transition:background .15s}
    @media(hover:hover){.cmt-anchor:hover{background:var(--ss-brand-tint-border)}}
    .cmt-quote{font-style:italic;color:var(--ss-text-subtle);border-left:2px solid var(--ss-brand);padding-left:6px;display:block;white-space:pre-wrap;word-break:break-word;max-height:3.6em;overflow:hidden;cursor:default}
    .cmt-actions{display:flex;gap:2px;margin-top:4px;opacity:.55;transition:opacity .15s}
    .cmt:hover .cmt-actions{opacity:1}
    .cmt-act{background:none;border:none;color:var(--ss-text-subtle);font-size:.7rem;cursor:pointer;padding:2px 6px;border-radius:3px;transition:background .15s,color .15s}
    @media(hover:hover){.cmt-act:hover{background:var(--ss-surface-soft);color:var(--ss-text-muted)}}
    .reactions{display:flex;flex-wrap:wrap;gap:3px;margin-top:4px}
    .cmt-foot{display:flex;flex-wrap:wrap;align-items:center;gap:4px 6px;margin-top:6px}
    .cmt-foot>.reactions,.cmt-foot>.cmt-actions{margin-top:0}
    .rxn{background:var(--ss-bg);border:1px solid var(--ss-border);border-radius:12px;padding:1px 7px;font-size:.75rem;cursor:pointer;display:flex;align-items:center;gap:3px;transition:border-color .15s}
    @media(hover:hover){.rxn:hover{border-color:var(--ss-border-strong)}}
    .rxn.mine{border-color:var(--ss-brand);background:var(--ss-brand-tint)}
    .rxn-count{font-size:.7rem;color:var(--ss-text-subtle)}
    .resolve-bar{width:100%;border:none;font-family:inherit;text-align:left;padding:4px .75rem;background:var(--ss-success-tint);border-top:1px solid var(--ss-success-tint-border);font-size:.7rem;color:var(--ss-success-text);display:flex;align-items:center;gap:4px;cursor:pointer;border-radius:0 0 8px 8px}
    /* Same bar, stating the fact rather than offering the switch, for a reader who
       may comment here but isn't one of the space's people. */
    .resolve-bar.done{cursor:default}
    .resolve-bar .rb-act{margin-left:auto;font-weight:600;text-decoration:underline;text-underline-offset:2px}
    .resolve-bar:focus-visible{outline:2px solid var(--ss-focus);outline-offset:-2px}
    .cmt-confirm{margin-top:6px;padding:10px 12px;border-radius:8px;background:var(--ss-danger-tint);border:1px solid var(--ss-danger-tint-border);font-size:.8rem;color:var(--ss-text)}
    .cmt-confirm p{margin:0 0 8px;line-height:1.4}
    .cmt-confirm-row{display:flex;gap:8px}
    .cmt-confirm .ss-btn{min-height:36px}
    .sb-allres{padding:1.25rem .75rem .5rem;text-align:center;font-size:.8rem;color:var(--ss-text-muted)}
    .resolved-divider{width:100%;background:none;border:none;font-family:inherit;padding:.5rem .75rem;font-size:.7rem;color:var(--ss-text-subtle);text-transform:uppercase;letter-spacing:.05em;cursor:pointer;display:flex;align-items:center;gap:6px}
    .resolved-divider:focus-visible{outline:2px solid var(--ss-focus);outline-offset:-2px}
    .resolved-divider::before,.resolved-divider::after{content:"";flex:1;border-top:1px solid var(--ss-border)}
    .sb-compose{padding:.75rem;border-top:1px solid var(--ss-border);flex-shrink:0}
    .compose-err{margin:0 0 6px;font-size:.75rem;color:var(--ss-danger-text,var(--ss-danger))}
    .compose-anchor{display:flex;align-items:center;gap:4px;margin-bottom:6px;font-size:.75rem;color:var(--ss-text-faint)}
    .compose-anchor-pill{background:var(--ss-brand-tint);color:var(--ss-brand-text);padding:2px 8px;border-radius:3px;display:inline-flex;align-items:center;gap:4px;max-width:100%;overflow:hidden}
    .sel-quote{font-style:italic;color:var(--ss-text-subtle);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:280px}
    .compose-anchor-pill .remove{cursor:pointer;color:var(--ss-text-faint);font-size:.8rem}
    .compose-anchor-pill .remove:hover{color:var(--ss-danger-text)}
    .pin-btn{background:none;border:1px dashed var(--ss-border-strong);color:var(--ss-text-subtle);border-radius:4px;padding:2px 8px;font-size:.72rem;cursor:pointer;transition:border-color .15s,color .15s;margin-bottom:6px}
    .pin-btn:hover{border-color:var(--ss-border-strong);color:var(--ss-text-muted)}
    .pin-btn.active{border-color:var(--ss-brand);color:var(--ss-brand-text);border-style:solid}
    .compose-reply{font-size:.72rem;color:var(--ss-text-faint);margin-bottom:4px;display:flex;align-items:center;gap:4px}
    .compose-reply .cancel{cursor:pointer;color:var(--ss-text-faint)}
    @media(hover:hover){.compose-reply .cancel:hover{color:var(--ss-danger-text)}}
    .compose-row{display:flex;gap:6px;position:relative;align-items:flex-end}
    .compose-row textarea{flex:1;min-width:0;background:var(--ss-bg);border:1px solid var(--ss-border-strong);border-radius:6px;color:var(--ss-text);padding:8px 10px;font-size:.82rem;font-family:inherit;resize:none;min-height:38px;line-height:1.4;transition:border-color .15s}
    .compose-row textarea:focus{outline:none;border-color:var(--ss-brand)}
    /* Annotate: the switch in the rail's header. On, a click on the page pins a
       comment; off, the page reads and its links work. C toggles it. */
    .sb-hdr-r{display:flex;align-items:center;gap:6px;margin-left:auto}
    .sb-sw{display:inline-flex;align-items:center;gap:8px;height:30px;padding:0 5px 0 10px;border-radius:999px;border:1px solid var(--ss-border);background:var(--ss-surface);font:600 .74rem/1 inherit;font-family:inherit;color:var(--ss-text-muted);cursor:pointer;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}
    .sb-sw .tg{position:relative;width:28px;height:16px;border-radius:999px;background:var(--ss-border-strong);transition:background .15s;flex:none}
    .sb-sw .tg::after{content:"";position:absolute;left:2px;top:2px;width:12px;height:12px;border-radius:999px;background:var(--ss-surface);box-shadow:0 1px 2px rgba(0,0,0,.2);transition:transform .15s}
    .sb-sw[aria-checked="true"]{border-color:var(--ss-brand-tint-border);background:var(--ss-brand-tint);color:var(--ss-brand-text)}
    .sb-sw[aria-checked="true"] .tg{background:var(--ss-brand)}
    .sb-sw[aria-checked="true"] .tg::after{transform:translateX(12px)}
    .sb-sw:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .sb-order{margin-left:auto;display:inline-flex;align-items:center;gap:2px;font-size:.72rem;color:var(--ss-text-subtle)}
    .sb-sort{display:inline-flex;align-items:center;gap:4px;padding:3px 5px 3px 7px;border:none;border-radius:6px;background:none;font:inherit;font-weight:500;color:var(--ss-text-muted);cursor:pointer}
    .sb-sort svg{display:block;color:var(--ss-text-faint);transition:transform .12s ease}
    .sb-sort:hover,.sb-sort[aria-expanded="true"]{background:var(--ss-surface-muted);color:var(--ss-text)}
    .sb-sort[aria-expanded="true"] svg{transform:rotate(180deg)}
    .sb-sort:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .sb-prefs{display:flex;align-items:center}
    /* A card opens with where it is: the kind of block, its words, and the way there. */
    .cmt-where{display:flex;align-items:center;gap:8px;width:100%;border:none;background:none;text-align:left;padding:.45rem .7rem .4rem;border-bottom:1px solid var(--ss-border-soft);border-radius:8px 8px 0 0;font:inherit;cursor:pointer;color:var(--ss-text-muted)}
    @media(hover:hover){.cmt-where:not(.is-page):hover{background:var(--ss-surface-soft)}}
    .cmt-where.is-page{cursor:default}
    .cmt-where:focus-visible{outline:2px solid var(--ss-focus);outline-offset:-2px}
    .cmt-kind{flex:none;font-size:.62rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--ss-text-subtle)}
    .cmt-ex{flex:1;min-width:0;font-size:.76rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .cmt-ex.is-q{font-style:italic;color:var(--ss-warn-text);background:var(--ss-warn-tint);border-radius:3px;padding:0 4px;flex:0 1 auto}
    .cmt-go{flex:none;margin-left:auto;color:var(--ss-text-subtle);opacity:0;transition:opacity .12s;font-size:.8rem}
    .cmt-where:hover .cmt-go,.thread.hot .cmt-go,.thread.focus .cmt-go{opacity:1}
    /* A touch screen has no hover to reveal the way there. */
    @media (hover:none){.cmt-go{opacity:1}}
    .thread{transition:border-color .15s,box-shadow .15s}
    .thread.hot{border-color:var(--ss-brand-tint-border);box-shadow:0 0 0 3px var(--ss-brand-tint)}
    .thread.focus{border-color:var(--ss-brand);box-shadow:0 0 0 3px var(--ss-brand-tint)}
    /* The Co-shepherd in a card: the same avatar mark it has in the panel, an AGENT
       tag, a working row while it holds the turn, and the change it made as a
       green row with Undo — greyed once undone, naming the version that did it. */
    .cmt-agent-av{background:#f6f2e8!important;box-shadow:inset 0 0 0 1px var(--ss-border-strong);display:inline-flex;align-items:center;justify-content:center}
    .cmt-agent-av svg,.mention-opt .mo-av.agent svg{width:72%;height:72%;display:block}
    .agent-tag{font-size:.56rem;font-weight:700;letter-spacing:.05em;padding:1px 5px;border-radius:4px;background:var(--ss-inverse-surface);color:var(--ss-inverse-text)}
    .cmt-work{display:flex;align-items:center;gap:6px;font-size:.78rem;color:var(--ss-brand-text);margin-top:2px}
    .cmt-work .dots{display:inline-flex;gap:3px;margin-left:2px;vertical-align:middle}
    .cmt-work .dots i{width:4px;height:4px;border-radius:999px;background:currentColor;animation:cmtBlink 1s infinite}
    .cmt-work .dots i:nth-child(2){animation-delay:.15s}
    .cmt-work .dots i:nth-child(3){animation-delay:.3s}
    @keyframes cmtBlink{0%,100%{opacity:.25}50%{opacity:1}}
    .cmt-fail{font-size:.76rem;color:var(--ss-danger-text);margin-top:2px}
    .cmt-retry{display:flex;align-items:center;gap:4px;background:none;border:0;padding:0;margin:8px 0 2px;font:inherit;font-weight:600;color:var(--ss-text-subtle);cursor:pointer;white-space:nowrap}
    .cmt-retry:hover,.cmt-retry:focus-visible{color:var(--ss-text);text-decoration:underline}
    .cmt-retry:disabled{opacity:.5;cursor:default;text-decoration:none}
    .cmt-did{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:6px;padding:6px 8px;border-radius:8px;background:var(--ss-success-tint);border:1px solid var(--ss-success-tint-border);font-size:.76rem;color:var(--ss-success-text)}
    .cmt-did.undone{background:var(--ss-surface-soft);border-color:var(--ss-border);color:var(--ss-text-subtle)}
    .cmt-did code{font:inherit;font-weight:600}
    .cmt-did>span{flex:1;min-width:110px}
    .cmt-did-a{border:1px solid var(--ss-success-tint-border);background:var(--ss-surface);color:var(--ss-success-text);font:600 .7rem/1 inherit;font-family:inherit;border-radius:999px;padding:5px 9px;cursor:pointer;text-decoration:none}
    @media(hover:hover){.cmt-did-a:hover{border-color:var(--ss-success-text)}}
    .cmt-did-a:disabled{opacity:.5;cursor:default}
    .mention.agent{background:var(--ss-inverse-surface);color:var(--ss-inverse-text);text-decoration:none;padding:0 5px;border-radius:5px}
    /* The sheep on a dark chip or pill keeps its own light plate: it is dark itself. */
    .mention.agent svg,.cmp-agent svg{flex:none;width:1em;height:1em;padding:1px;border-radius:3px;background:#f6f2e8}
    .mention.agent svg{margin-right:4px;vertical-align:-.18em}
    .mention-opt.agent .mo-h{color:var(--ss-text);flex:none}
    .mention-opt .mo-s{color:var(--ss-text-faint);font-size:.72rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
    .mention-opt .mo-av.agent{background:#f6f2e8;box-shadow:inset 0 0 0 1px var(--ss-border-strong);display:inline-flex;align-items:center;justify-content:center}
    /* "@session": the coding session that made the page. A shell prompt in the
       words and the menu; its reply wears its harness's mark on a plain plate. */
    .mention.agent.sess svg{background:none;padding:0}
    .cmt-sess-av,.mention-opt .mo-av.sess{background:var(--ss-surface)!important;color:var(--ss-text);box-shadow:inset 0 0 0 1px var(--ss-border-strong);display:inline-flex;align-items:center;justify-content:center}
    .cmt-sess-av svg,.mention-opt .mo-av.sess svg{width:64%;height:64%;display:block}
    .cmt-work.is-idle{color:var(--ss-text-subtle);flex-wrap:wrap}
    .cmt-sess-act{font-size:.76rem;margin-top:4px}
    a.agent-tag{text-decoration:none}
    a.agent-tag:hover,a.agent-tag:focus-visible{text-decoration:underline}
    .mention-sec{font-size:.6rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ss-text-faint);padding:6px 10px 2px;font-weight:700}
    /* The floating composer: the dark pill that opens where the page was clicked,
       over the frame, and follows its spot as the page scrolls. The draft lives
       here, in the shell, never in the author's page. */
    .main-row{position:relative}
    /* The writer's status (live op "status"): the agent building this page says what
       it is doing, and every open tab shows it here, over the frame, until the next
       publish takes it down. Shell chrome, never the author's page. The script centres
       it on the frame (workPlace), so an open rail or Co-shepherd can't push it off. */
    .ss-work{position:absolute;z-index:30;left:50%;bottom:calc(14px + env(safe-area-inset-bottom,0px));width:460px;max-width:calc(100% - 24px);box-sizing:border-box;padding:12px 12px 10px;border-radius:16px;background:var(--ss-surface);color:var(--ss-text);border:1px solid var(--ss-border);box-shadow:var(--ss-elev-pop);font-size:.84rem;line-height:1.35;opacity:0;transform:translate(-50%,14px);transition:opacity .3s ease,transform .3s ease}
    .ss-work.is-fixed{position:fixed}
    .ss-work.is-in{opacity:1;transform:translate(-50%,0)}
    @property --ss-work-a{syntax:"<angle>";inherits:false;initial-value:0deg}
    .ss-work::before{content:"";position:absolute;inset:-1px;border-radius:inherit;padding:1.5px;pointer-events:none;background:conic-gradient(from var(--ss-work-a),transparent 0deg,var(--ss-brand) 70deg,var(--ss-brand-text) 120deg,transparent 190deg);-webkit-mask:linear-gradient(var(--ss-text) 0 0) content-box,linear-gradient(var(--ss-text) 0 0);-webkit-mask-composite:xor;mask-composite:exclude;animation:ssWorkSpin 3.6s linear infinite}
    @keyframes ssWorkSpin{to{--ss-work-a:360deg}}
    .ss-work.is-stopped::before,.ss-work.is-quiet::before,.ss-work.is-done::before{display:none}
    .ss-work-top{display:flex;align-items:center;gap:10px}
    .ss-work-sheep{position:relative;flex:none;width:52px;height:37px}
    .ss-work-sheep img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;opacity:0}
    .ss-work-sheep img.on{opacity:1}
    .ss-work.is-stopped .ss-work-sheep,.ss-work.is-quiet .ss-work-sheep{filter:grayscale(1);opacity:.55}
    .ss-work-ok{display:none;position:absolute;inset:4px 10px;align-items:center;justify-content:center;border-radius:50%;background:var(--ss-success-tint);color:var(--ss-success-text);border:1px solid var(--ss-success-tint-border)}
    .ss-work.is-done .ss-work-ok{display:flex}
    .ss-work.is-done .ss-work-sheep img{display:none}
    .ss-work-head{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
    .ss-work-title{font-weight:650;font-size:.88rem;overflow-wrap:anywhere}
    .ss-work-sub{color:var(--ss-text-subtle);font-size:.78rem}
    .ss-work-clock{flex:none;align-self:flex-start;padding-top:2px;font-size:.75rem;font-variant-numeric:tabular-nums;color:var(--ss-text-faint)}
    .ss-work-min{flex:none;align-self:flex-start;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -4px 0 -4px;padding:0;border:0;border-radius:8px;background:transparent;color:var(--ss-text-faint);cursor:pointer}
    .ss-work-min:hover{background:var(--ss-surface-soft);color:var(--ss-text)}
    .ss-work-min svg{transition:transform .2s ease}
    .ss-work.is-min .ss-work-min svg{transform:rotate(180deg)}
    .ss-work-x{display:none;flex:none;align-self:flex-start;align-items:center;justify-content:center;width:28px;height:28px;margin:-4px -4px 0 0;padding:0;border:0;border-radius:8px;background:transparent;color:var(--ss-text-faint);cursor:pointer}
    .ss-work-x:hover{background:var(--ss-surface-soft);color:var(--ss-text)}
    .ss-work.is-idle .ss-work-x{display:inline-flex}
    .ss-work.is-done .ss-work-x,.ss-work.is-screen .ss-work-x{display:none}
    .ss-work-now{display:flex;align-items:center;gap:9px;margin-top:10px;padding:8px 10px;border-radius:10px;background:var(--ss-brand-tint);color:var(--ss-brand-text);font-weight:550}
    .ss-work-now.is-new{animation:ssWorkIn .35s ease-out}
    .ss-work-txt{min-width:0;overflow-wrap:anywhere}
    @supports ((-webkit-background-clip:text) or (background-clip:text)) and (color:color-mix(in srgb,red,blue)){
      .ss-work-txt{background:linear-gradient(90deg,var(--ss-brand-text) 38%,color-mix(in srgb,var(--ss-brand-text) 30%,transparent) 50%,var(--ss-brand-text) 62%);background-size:260% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:ssWorkShine 2.6s linear infinite}
      .ss-work.is-stopped .ss-work-txt,.ss-work.is-quiet .ss-work-txt{background:none;color:inherit;animation:none}
    }
    @keyframes ssWorkShine{from{background-position:100% 0}to{background-position:0 0}}
    @keyframes ssWorkIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
    .ss-work.is-stopped .ss-work-now,.ss-work.is-quiet .ss-work-now{background:var(--ss-surface-muted);color:var(--ss-text-muted)}
    .ss-work.is-stopped .ss-dots,.ss-work.is-stopped .ss-work-clock{display:none}
    .ss-work.is-quiet .ss-dots i{animation:none;opacity:.4}
    .ss-work-steps{list-style:none;margin:6px 0 0;padding:0 10px}
    .ss-work-steps li{display:flex;align-items:center;gap:9px;padding:3px 0;color:var(--ss-text-subtle);font-size:.8rem}
    .ss-work-steps li.is-new{animation:ssWorkIn .35s ease-out}
    .ss-work-ck{flex:none;display:inline-flex;width:15px;justify-content:center;color:var(--ss-success-text)}
    .ss-work-st{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .ss-work-dur{flex:none;font-size:.72rem;font-variant-numeric:tabular-nums;color:var(--ss-text-faint)}
    .ss-work-steps:empty,.ss-work.is-min .ss-work-steps,.ss-work.is-min .ss-work-sub{display:none}
    .ss-work.is-done .ss-work-now,.ss-work.is-done .ss-work-steps,.ss-work.is-done .ss-work-clock,.ss-work.is-done .ss-work-min{display:none}
    @media(prefers-reduced-motion:reduce){
      .ss-work,.ss-work-now.is-new,.ss-work-steps li.is-new{transition:none;animation:none}
      .ss-work::before{animation:none}
      .ss-work-txt{animation:none!important}
      .ss-work:not(.is-stopped):not(.is-quiet):not(.is-done) .ss-work-sheep{animation:ss-nav-sheep-pulse 1.8s ease-in-out infinite}
    }
    @media(max-width:640px){.ss-work{padding:10px 10px 8px;border-radius:14px}.ss-work-sub{font-size:.75rem}}
    .ss-work.is-lifted{bottom:calc(80px + env(safe-area-inset-bottom,0px))}
    .ss-work-foot{display:none}
    /* Screen mode: a live link's page is only a placeholder, so the work IS the page —
       one simple "Spacesheep is working on it" screen over the whole frame (placed by
       workPlace) until a real page arrives, when it becomes the card again. */
    .ss-work.is-screen{bottom:auto;transform:none;border:0;border-radius:0;box-shadow:none;background:var(--ss-bg);padding:32px 20px;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;overflow:auto}
    .ss-work.is-screen.is-in{transform:none}
    .ss-work.is-screen::before{display:none}
    .ss-work.is-screen .ss-work-top{flex-direction:column;gap:0}
    .ss-work.is-screen .ss-work-sheep{width:150px;height:106px;margin-bottom:24px}
    .ss-work.is-screen .ss-work-ok{inset:auto;left:39px;top:17px;width:72px;height:72px}
    .ss-work.is-screen .ss-work-ok svg{width:34px;height:34px}
    .ss-work.is-screen .ss-work-head{flex:none;align-items:center;gap:8px}
    .ss-work.is-screen .ss-work-title{font-size:clamp(1.5rem,4.2vw,2.15rem);font-weight:700;letter-spacing:-.01em;line-height:1.15}
    .ss-work.is-screen .ss-work-sub{font-size:1rem;color:var(--ss-text-muted);max-width:34em;overflow-wrap:anywhere}
    .ss-work.is-screen .ss-work-clock,.ss-work.is-screen .ss-work-min{display:none}
    .ss-work.is-screen .ss-work-now{margin-top:26px;padding:10px 18px;border-radius:999px;font-size:.98rem;max-width:min(560px,100%)}
    .ss-work.is-screen .ss-work-steps{margin-top:14px;padding:0;width:min(440px,100%)}
    .ss-work.is-screen .ss-work-steps li{justify-content:center}
    .ss-work.is-screen .ss-work-st{flex:0 1 auto}
    .ss-work.is-screen .ss-work-foot{display:block;margin-top:22px;font-size:.8rem;color:var(--ss-text-faint);font-variant-numeric:tabular-nums}
    .ss-work.is-screen.is-done .ss-work-foot{display:none}
    @media print{.ss-work{display:none}}
    .cmp{position:absolute;z-index:45;width:400px;max-width:calc(100% - 24px);border-radius:22px;padding:12px 8px 8px 18px;background:var(--ss-inverse-surface);color:var(--ss-inverse-text);box-shadow:0 0 0 1px rgba(255,255,255,.1),0 14px 34px -10px rgba(10,8,40,.55);animation:cmpPop .18s ease-out}
    .cmp[hidden]{display:none}
    .cmp.shake{animation:cmpShake .32s}
    @keyframes cmpPop{from{transform:scale(.96) translateY(4px);opacity:0}to{transform:none;opacity:1}}
    @keyframes cmpShake{0%,100%{transform:none}25%{transform:translateX(-5px)}75%{transform:translateX(5px)}}
    .cmp-q{font-size:.76rem;color:var(--ss-inverse-text-subtle);border-left:2px solid var(--ss-brand-tint-border);padding-left:8px;margin:0 12px 8px 0;font-style:italic;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .cmp-q[hidden]{display:none}
    .cmp .compose-row textarea{background:transparent;border:none;color:var(--ss-inverse-text);font-size:.92rem;padding:4px 8px 4px 0;min-height:30px;caret-color:var(--ss-inverse-text)}
    .cmp .compose-row textarea::placeholder{color:var(--ss-inverse-text-subtle)}
    .cmp .compose-row textarea:focus{border:none;box-shadow:none}
    .cmp .ss-send-enter{color:var(--ss-inverse-text-subtle);height:30px}
    .cmp .ss-send-enter:hover:not(:disabled){color:var(--ss-inverse-text);background:rgba(255,255,255,.1)}
    .cmp .ss-send-enter:disabled{color:var(--ss-inverse-text-subtle);opacity:.45}
    .cmp .mention-menu{bottom:auto;top:calc(100% + 8px);box-shadow:var(--ss-elev-pop)}
    .cmp-row{display:flex;align-items:center;gap:8px;border-top:1px solid rgba(255,255,255,.09);padding-top:7px;margin:6px 4px 0 -10px}
    .cmp-hint{flex:1;font-size:.7rem;color:var(--ss-inverse-text-subtle);white-space:nowrap;overflow:hidden}
    /* A touch keyboard has no Esc and no Shift+Enter: the keys aren't news there. */
    @media(hover:none){.cmp-hint{visibility:hidden}.inline-reply .inline-hint,.cmt-edit .inline-hint{display:none}}
    .cmp-hint b{font-weight:400;opacity:.5;margin:0 3px}
    .cmp-hint kbd{font:600 .62rem/1 inherit;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:17px;padding:0 5px;border-radius:5px;background:rgba(255,255,255,.1);color:var(--ss-inverse-text);margin-right:3px}
    .cmp-agent{flex:1;display:flex;align-items:center;gap:6px;font-size:.74rem;color:var(--ss-inverse-text);white-space:nowrap;overflow:hidden}
    .cmp-agent[hidden]{display:none}
    .cmp-agent span{color:var(--ss-inverse-text-subtle)}
    .cmp-at{width:28px;height:28px;border-radius:999px;border:none;background:rgba(255,255,255,.08);color:var(--ss-inverse-text);font:600 .85rem/1 inherit;font-family:inherit;cursor:pointer}
    @media(hover:hover){.cmp-at:hover{background:rgba(255,255,255,.16)}}
    .cmp-x{width:28px;height:28px;border-radius:999px;border:none;background:none;color:var(--ss-inverse-text-subtle);font-size:1.1rem;line-height:1;cursor:pointer}
    @media(hover:hover){.cmp-x:hover{color:var(--ss-inverse-text)}}
    .cmp-on{display:flex;align-items:center;gap:6px;font-size:.74rem;color:var(--ss-inverse-text-subtle);margin:0 12px 6px 0;min-width:0}
    .cmp-on[hidden]{display:none}
    .cmp-on b{font-weight:600;color:var(--ss-inverse-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
    .cmp-wide{flex:none;border:none;border-radius:999px;background:rgba(255,255,255,.1);color:var(--ss-inverse-text);font:600 .7rem/1 inherit;font-family:inherit;padding:6px 10px;min-height:26px;cursor:pointer}
    @media(hover:hover){.cmp-wide:hover{background:rgba(255,255,255,.18)}}
    .cmp-wide:focus-visible{outline:2px solid var(--ss-brand-tint-border);outline-offset:2px}
    .cmp-gone{font-size:.72rem;color:var(--ss-warn);margin:0 0 6px}
    .cmp-gone[hidden]{display:none}
    .sb-tab,.sb-scrim{display:none}
    .sb-toast{position:absolute;left:50%;bottom:14px;transform:translate(-50%,8px);opacity:0;z-index:46;background:var(--ss-inverse-surface);color:var(--ss-inverse-text);font-size:.78rem;padding:8px 14px;border-radius:999px;transition:opacity .2s,transform .2s;pointer-events:none;box-shadow:var(--ss-elev-pop);white-space:nowrap;max-width:calc(100% - 24px);overflow:hidden;text-overflow:ellipsis}
    .sb-toast.on{opacity:1;transform:translate(-50%,0)}
    .sb-toast.has-act{display:flex;align-items:center;gap:10px;padding:4px 6px 4px 14px}
    .sb-toast.has-act.on{pointer-events:auto}
    .sb-toast button{background:none;border:none;color:inherit;font:inherit;font-weight:700;text-decoration:underline;text-underline-offset:2px;cursor:pointer;padding:6px 8px;min-height:32px;border-radius:999px}
    .sb-toast button:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .ss-send{position:relative;flex-shrink:0;width:38px;height:38px;border-radius:50%;border:none;background:var(--ss-brand);color:var(--ss-inverse-text);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .15s,transform .08s;padding:0}
.ss-send:hover:not(:disabled){background:var(--ss-brand-hover)}
.ss-send:active:not(:disabled){transform:scale(.93)}
.ss-send:disabled{opacity:.45;cursor:default}
.ss-send svg{width:18px;height:18px;display:block}
.ss-send--end{margin-left:auto}
.ss-send.working{opacity:1}
.ss-send.working svg{visibility:hidden}
.ss-send.working::after{content:"";position:absolute;width:16px;height:16px;border:2px solid rgba(255,255,255,.45);border-top-color:var(--ss-surface);border-radius:50%;animation:ss-send-spin .6s linear infinite}
@keyframes ss-send-spin{to{transform:rotate(360deg)}}
.ss-pill-field{display:flex;gap:0;align-items:flex-end;background:var(--ss-surface);border:1px solid var(--ss-border-strong);border-radius:20px;padding:4px 6px 4px 4px;box-shadow:var(--ss-elev-1);transition:border-color .15s}
.ss-pill-field:focus-within{border-color:var(--ss-brand)}
.ss-pill-field textarea{flex:1;min-width:0;background:transparent;border:none;border-radius:0;color:var(--ss-text);padding:9px 6px 9px 10px;font-size:.82rem;font-family:inherit;resize:none;min-height:38px;line-height:1.4}
.ss-pill-field textarea:focus{outline:none;border:none}
.ss-send-enter{flex-shrink:0;width:34px;height:38px;border:none;border-radius:14px;background:none;color:var(--ss-text-muted);display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0;transition:color .15s,background .15s}
.ss-send-enter:hover:not(:disabled){color:var(--ss-brand-text);background:var(--ss-surface-soft)}
.ss-send-enter:disabled{color:var(--ss-text-faint);opacity:.6;cursor:default}
.ss-send-enter:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
.ss-send-enter svg{display:block}
    .ss-copy{position:relative;display:inline-flex;align-items:center;gap:.3rem;border:1px solid transparent;border-radius:6px;background:none;color:var(--ss-text-subtle);font:inherit;line-height:1;cursor:pointer;transition:background .12s,color .12s,border-color .12s}
.ss-copy--sm{padding:3px;font-size:.72rem}
.ss-copy--md{padding:6px 10px;font-size:.8rem;color:var(--ss-text-muted);background:var(--ss-surface);border-color:var(--ss-border-strong)}
.ss-copy:hover{background:var(--ss-surface-soft);color:var(--ss-text-muted)}
.ss-copy--md:hover{background:var(--ss-surface-muted);border-color:var(--ss-border-strong)}
.ss-copy:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
.ss-copy:disabled{opacity:.5;cursor:default}
.ss-copy.is-copied,.ss-copy.is-copied:hover{color:var(--ss-success-text)}
.ss-copy.is-failed,.ss-copy.is-failed:hover{color:var(--ss-danger-text)}
.ss-copy-ic{position:relative;display:inline-flex;flex-shrink:0;width:14px;height:14px}
.ss-copy--md .ss-copy-ic{width:16px;height:16px}
.ss-copy-g{position:absolute;top:0;left:0;width:100%;height:100%;display:block;transition:opacity .12s,transform .12s}
.ss-copy-g-done{opacity:0;transform:scale(.7)}
.ss-copy.is-copied .ss-copy-g-idle{opacity:0;transform:scale(.7)}
.ss-copy.is-copied .ss-copy-g-done{opacity:1;transform:none}
.ss-copy-label{white-space:nowrap}
/* Screen-reader announcement alongside the visible confirmation. */
.ss-copy-live{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
@media (prefers-reduced-motion: reduce){.ss-copy-g{transition:none}}
    :root {
  --ss-color-brand: var(--ss-brand, #5a5ae6);
  --ss-color-brand-hover: var(--ss-brand-hover, #4a4ad6);
  --ss-color-text: var(--ss-text, #1a1a1f);
  --ss-color-text-muted: var(--ss-text-muted, #4a4a58);
  --ss-color-text-subtle: var(--ss-text-faint, #888);
  --ss-color-border: var(--ss-border-strong, #d0cdc8);
  --ss-color-surface: var(--ss-surface, #fff);
  --ss-color-surface-muted: var(--ss-surface-muted, #f4f2ee);
  --ss-color-surface-soft: var(--ss-surface-soft, #eeecea);
  --ss-color-surface-soft-hover: var(--ss-surface-soft-hover, #e0ddd8);
  --ss-color-success: var(--ss-success, #2d8f5e);
  --ss-color-success-hover: var(--ss-success-text, #26794f);
  --ss-color-danger: var(--ss-danger, #cc4444);
  --ss-color-danger-text: var(--ss-danger-text, #b4453a);
  --ss-color-danger-border: var(--ss-danger-tint-border, #e6c4c0);
  --ss-color-danger-bg-hover: var(--ss-danger-tint, #fbf0ef);
  --ss-color-danger-border-hover: var(--ss-danger-tint-border, #d99b93);
  --ss-color-on-brand: var(--ss-on-brand, #fff);
  /* Brand as INK on the page, which is a different value from the fill in the
     dark theme and the same one on paper. */
  --ss-color-brand-text: var(--ss-brand-text, #5a5ae6);
  --ss-radius-sm: 4px;
  --ss-radius-md: 6px;
  --ss-radius-lg: 8px;
  --ss-btn-font-weight: 500;
  --ss-btn-transition: background .12s, border-color .12s, color .12s;
  --ss-btn-md-pad-y: 6px;
  --ss-btn-md-pad-x: 14px;
  --ss-btn-md-font-size: 0.82rem;
  --ss-btn-sm-pad-y: 2px;
  --ss-btn-sm-pad-x: 8px;
  --ss-btn-sm-font-size: 0.72rem;
  --ss-btn-lg-pad-y: 0.55rem;
  --ss-btn-lg-pad-x: 1.1rem;
  --ss-btn-lg-font-size: 0.9rem;
  /* Gloss (opt-in, see the 'gloss' option on button()). One full cycle: the sweep is a
     fraction of it and the rest is stillness — a highlight that never stops sliding
     stops reading as a highlight and starts reading as a page that's still loading. */
  --ss-btn-gloss-cycle: 6s;
  --ss-btn-gloss-delay: 1.5s;
  --ss-btn-gloss-sheen: rgba(255, 255, 255, 0.38);
}
.ss-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  font-family: var(--ss-font-sans, inherit); font-weight: var(--ss-btn-font-weight);
  border-radius: var(--ss-radius-md); cursor: pointer; text-decoration: none;
  transition: var(--ss-btn-transition); line-height: 1.2; box-sizing: border-box;
}
.ss-btn:disabled,
.ss-btn.ss-btn--disabled { opacity: 0.5; cursor: default; pointer-events: none; }

.ss-btn[hidden] { display: none; }
.ss-btn--md { padding: var(--ss-btn-md-pad-y) var(--ss-btn-md-pad-x); font-size: var(--ss-btn-md-font-size); }
.ss-btn--sm { padding: var(--ss-btn-sm-pad-y) var(--ss-btn-sm-pad-x); font-size: var(--ss-btn-sm-font-size); border-radius: var(--ss-radius-sm); }
.ss-btn--lg { padding: var(--ss-btn-lg-pad-y) var(--ss-btn-lg-pad-x); font-size: var(--ss-btn-lg-font-size); border-radius: var(--ss-radius-lg); }

.ss-btn--primary { background: var(--ss-color-brand); color: var(--ss-color-on-brand); border: none; }
.ss-btn--primary:hover:not(:disabled):not(.ss-btn--disabled) { background: var(--ss-color-brand-hover); }
.ss-btn--ghost {
  background: var(--ss-color-surface); color: var(--ss-color-text-muted);
  border: 1px solid var(--ss-color-border);
}
.ss-btn--ghost:hover:not(:disabled):not(.ss-btn--disabled) { background: var(--ss-color-surface-muted); }
.ss-btn--outline {
  background: none; color: var(--ss-color-text-subtle);
  border: 1px solid var(--ss-color-border);
}
.ss-btn--outline:hover:not(:disabled):not(.ss-btn--disabled) {
  border-color: var(--ss-color-brand-text); color: var(--ss-color-brand-text);
}

.ss-btn--success { background: var(--ss-color-success); color: var(--ss-color-surface); border: none; }
.ss-btn--success:hover:not(:disabled):not(.ss-btn--disabled) { background: var(--ss-color-success-hover); }
.ss-btn--soft {
  background: var(--ss-color-surface-soft); color: var(--ss-color-text-muted); border: none;
}
.ss-btn--soft:hover:not(:disabled):not(.ss-btn--disabled) { background: var(--ss-color-surface-soft-hover); }
.ss-btn--danger {
  background: none; color: var(--ss-color-text-subtle);
  border: 1px solid var(--ss-color-border);
}
.ss-btn--danger:hover:not(:disabled):not(.ss-btn--disabled) {
  border-color: var(--ss-color-danger); color: var(--ss-color-danger);
}
.ss-btn--danger-solid {
  background: var(--ss-color-surface); color: var(--ss-color-danger-text);
  border: 1px solid var(--ss-color-danger-border);
}
.ss-btn--danger-solid:hover:not(:disabled):not(.ss-btn--disabled) {
  background: var(--ss-color-danger-bg-hover); border-color: var(--ss-color-danger-border-hover);
}

/* --- Gloss ---------------------------------------------------------------
   A narrow skewed highlight that crosses the button and then waits. The sweep
   is ~14% of the cycle; the remaining ~86% is stillness, which is what makes it
   read as an occasional glint rather than a progress bar.

   Transform-only (never 'left'), so it animates on the compositor and costs
   nothing on a page that is otherwise idle. overflow:hidden clips it to the
   button's own radius, and the pseudo-element is pointer-events:none so it
   can't eat the click it exists to attract. */
.ss-btn--gloss { position: relative; overflow: hidden; }
.ss-btn--gloss::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 45%;
  background: linear-gradient(100deg, transparent, var(--ss-btn-gloss-sheen), transparent);
  transform: translateX(-260%) skewX(-18deg);
  animation: ss-btn-gloss var(--ss-btn-gloss-cycle) var(--ss-btn-gloss-delay) ease-in-out infinite;
  pointer-events: none;
}
/* The sweep, then a long rest at the far side before the cycle repeats. */
@keyframes ss-btn-gloss {
  0% { transform: translateX(-260%) skewX(-18deg); }
  14%, 100% { transform: translateX(340%) skewX(-18deg); }
}
/* A disabled button has nothing to attract a click to, so it doesn't glint. */
.ss-btn--gloss:disabled::after,
.ss-btn--gloss.ss-btn--disabled::after { content: none; }
/* Motion is decoration here — the accent colour already does the attracting. */
@media (prefers-reduced-motion: reduce) {
  .ss-btn--gloss::after { animation: none; opacity: 0; }
}
    .ss-skel{display:flex;gap:.55rem;align-items:flex-start}
.ss-skel--end{flex-direction:row-reverse}
.ss-skel-avatar{flex-shrink:0;width:24px;height:24px;border-radius:50%}
.ss-skel-lines{flex:1;display:flex;flex-direction:column;gap:.4rem;min-width:0}
.ss-skel--end .ss-skel-lines{align-items:flex-end}
.ss-skel-line{height:.66rem;border-radius:6px;max-width:100%}
.ss-skel-input{display:flex;gap:6px;align-items:flex-end}
.ss-skel-field{flex:1;height:38px;border-radius:6px}
.ss-skel-send{flex-shrink:0;width:38px;height:38px;border-radius:50%}
.ss-skel-avatar,.ss-skel-line,.ss-skel-field,.ss-skel-send{
  background:linear-gradient(100deg,var(--ss-surface-soft) 30%,var(--ss-surface-muted) 50%,var(--ss-surface-soft) 70%);
  background-size:200% 100%;
  animation:ss-skel-shimmer 1.4s ease-in-out infinite;
}
@keyframes ss-skel-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
@keyframes ss-skel-pulse{0%{opacity:1}50%{opacity:.55}100%{opacity:1}}
.ss-skel-chat{display:flex;flex-direction:column;gap:1rem;padding:.35rem .25rem}
.ss-skel-chat .ss-skel-bubble .ss-skel-lines{gap:.35rem}
/* Reduce Motion drops the travelling gradient — that sweep is the actual motion —
   but keeps a plain opacity pulse, so a loading list still reads as loading rather
   than as a page of empty grey bars. */
@media (prefers-reduced-motion:reduce){
  .ss-skel-avatar,.ss-skel-line,.ss-skel-field,.ss-skel-send{
    background:var(--ss-surface-soft);
    animation:ss-skel-pulse 1.8s ease-in-out infinite;
  }
}
    .ss-pill-select { position: relative; display: inline-flex; align-items: center; max-width: min(72%, 280px); vertical-align: middle; flex-shrink: 1; min-width: 0; }
.ss-pill-select-trigger {
  display: none; align-items: center; gap: 0.45rem; max-width: 100%; min-width: 0;
  background: var(--ss-surface-muted); border: 1px solid var(--ss-border); border-radius: 999px;
  padding: 0.28rem 0.55rem 0.28rem 0.32rem; font: inherit; font-size: 0.82rem;
  color: var(--ss-text-muted); cursor: pointer; transition: background 0.15s, border-color 0.15s;
}
.ss-pill-select.is-ready .ss-pill-select-trigger { display: inline-flex; }
.ss-pill-select-trigger:hover { background: var(--ss-surface-soft); border-color: var(--ss-border-strong); }
.ss-pill-select-trigger:focus { outline: none; }
.ss-pill-select-trigger:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: 2px; }
.ss-pill-select.open .ss-pill-select-trigger { background: var(--ss-surface-soft); border-color: var(--ss-border-strong); }
.ss-pill-select-ic {
  flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%;
  background: var(--ss-brand); color: var(--ss-inverse-text); display: flex; align-items: center; justify-content: center;
  pointer-events: none;
}
.ss-pill-select-ic svg { width: 13px; height: 13px; display: block; }
.ss-pill-select-label {
  font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; pointer-events: none;
}
.ss-pill-select-caret {
  flex-shrink: 0; display: inline-flex; color: var(--ss-text-faint); pointer-events: none;
}
.ss-pill-select-caret svg { width: 12px; height: 12px; display: block; transition: transform 0.12s ease; }
.ss-pill-select.open .ss-pill-select-caret svg { transform: rotate(180deg); }
.ss-pill-select-setup {
  display: none; align-items: center; gap: 0.45rem;
  background: var(--ss-surface-muted); border: 1px solid var(--ss-border); border-radius: 999px;
  padding: 0.35rem 0.85rem; font-size: 0.82rem; font-weight: 500;
  color: var(--ss-text-muted); text-decoration: none;
}
.ss-pill-select.is-empty .ss-pill-select-setup { display: inline-flex; }
.ss-pill-select.is-loading .ss-pill-select-trigger { display: inline-flex; }
.ss-pill-select-setup:hover { background: var(--ss-surface-soft); border-color: var(--ss-border-strong); }
.ss-pill-select-menu {
  display: none; position: absolute; bottom: calc(100% + 6px); left: 0;
  min-width: max(100%, 220px); max-width: min(320px, 90vw); max-height: 280px;
  overflow-y: auto; background: var(--ss-surface); border: 1px solid var(--ss-border); border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0,0,0,.12); z-index: 50; padding: 0.3rem;
}
.ss-pill-select-menu.open { display: block; }
.ss-pill-select-menu .ss-pill-group { margin-bottom: 0.25rem; }
.ss-pill-select-menu .ss-pill-group:last-child { margin-bottom: 0; }
.ss-pill-select-menu .ss-pill-group-hdr {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--ss-text-faint); padding: 0.35rem 0.55rem 0.15rem;
}
.ss-pill-select-menu .ss-pill-row {
  display: block; width: 100%; text-align: left; border: none; background: transparent;
  padding: 0.4rem 0.55rem; font: inherit; font-size: 0.8rem; font-family: 'SF Mono','Fira Code',monospace;
  color: var(--ss-text-muted); cursor: pointer; border-radius: 6px;
}
.ss-pill-select-menu .ss-pill-row:hover { background: var(--ss-brand-tint); }
.ss-pill-select-menu .ss-pill-row.active,
.ss-pill-select-menu .ss-pill-row[aria-selected="true"] { background: var(--ss-brand-tint); color: var(--ss-brand-deep); }
.ss-pill-select-menu .ss-pill-empty { color: var(--ss-text-faint); font-size: 0.8rem; padding: 0.55rem; }
.agent-composer, .agent-composer-bar { overflow: visible; }
    .emoji-picker{position:fixed;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:8px;padding:6px;display:flex;flex-wrap:wrap;gap:2px;width:220px;box-shadow:0 4px 16px rgba(26,26,31,.12);z-index:1000}
.emoji-picker button{background:none;border:none;font-size:1.1rem;cursor:pointer;padding:4px;border-radius:4px;transition:background .1s}
@media(hover:hover){.emoji-picker button:hover{background:var(--ss-surface-soft)}}
@media(max-width:640px){.emoji-picker{width:min(280px,90vw)}}
@media(prefers-reduced-motion:reduce){.emoji-picker button{transition:none}}
    .ss-menu{position:fixed;z-index:1000;min-width:160px;max-width:min(300px,calc(100vw - 16px));padding:4px;display:flex;flex-direction:column;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:10px;box-shadow:var(--ss-elev-pop)}
.ss-mi{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:none;border:none;border-radius:6px;padding:7px 10px;font:inherit;font-size:.8rem;color:var(--ss-text);cursor:pointer;-webkit-tap-highlight-color:transparent}
.ss-mi:hover,.ss-mi:focus-visible{background:var(--ss-surface-soft);outline:none}
.ss-mi.danger{color:var(--ss-danger-text)}
.ss-mi-t{flex:1;min-width:0}
.ss-mi-ck{flex:none;display:flex;width:14px;color:var(--ss-brand-text);visibility:hidden}
.ss-mi-ck svg{display:block}
.ss-mi[aria-checked="true"] .ss-mi-ck{visibility:visible}
@media(hover:hover){.ss-mi.m-touch{display:none}}
@media(hover:none){.ss-mi{padding:11px 12px;font-size:.88rem}}
    /* The ⋯ menu shows only while it holds something the header had no room for —
       headerFitScript decides that by measuring, at every width, and marks the
       wrapper has-items. The menu-row labels ride along in the markup at every width,
       hidden until a button is actually inside the menu. */
    .more-wrap{display:none;position:relative;flex-shrink:0}
    .more-wrap.has-items{display:block}
    /* Tighter header: the Comments word steps aside for its count. */
    .doc-hdr.hdr-compact .cbtn-txt{display:none}
    .doc-hdr.hdr-compact .hdr-right{gap:.5rem}
    .doc-hdr.hdr-tight .share-label{display:none}
    .doc-hdr.hdr-tight #shareBtn{padding:4px 8px}
    .rate-wrap[hidden]{display:none}
    /* Speak sits in the bottom-right corner (assets/shell/speak.ts). Here it clears the
       admin tools strip, and steps left of the comment rail when that is a column
       beside the page. It stays under every panel (z 39 < the rail, the chat, the
       pinned composer). */
    body:has(.doc-tools) .speak{bottom:calc(48px + env(safe-area-inset-bottom,0px))}
    body:has(.doc-tools) .speak-pop{bottom:calc(112px + env(safe-area-inset-bottom,0px))}
    @media(min-width:641px){body:has(#sidebar:not(.hidden)) .speak,body:has(#sidebar:not(.hidden)) .speak-pop{right:calc(var(--sb-w,380px) + 16px)}}
    /* The comment button: the round one in the same corner, stacked over Speak while
       Speak sits in its own corner, alone there when Speak isn't on the page or has
       been dragged away. Tap: pin a comment without opening the rail; tap again: the
       rail. Hold: say it (Speak's roster). Drag: anywhere, kept per browser (inline
       styles win over every rule here). Its bubble peeks out beside it: a comment
       that just arrived, the words while you talk, what just posted. */
    .cfab{position:fixed;z-index:39;right:max(16px,env(safe-area-inset-right));bottom:calc(16px + env(safe-area-inset-bottom,0px))}
    body:has(.doc-tools) .cfab{bottom:calc(48px + env(safe-area-inset-bottom,0px))}
    body:has(#ssSpeak:not([style*="bottom"])) .cfab{bottom:calc(80px + env(safe-area-inset-bottom,0px))}
    body:has(.doc-tools):has(#ssSpeak:not([style*="bottom"])) .cfab{bottom:calc(112px + env(safe-area-inset-bottom,0px))}
    @media(min-width:641px){body:has(#sidebar:not(.hidden)) .cfab{right:calc(var(--sb-w,380px) + 16px)}}
    .cfab-btn{position:relative;display:inline-flex;align-items:center;justify-content:center;width:52px;height:52px;padding:0;border-radius:50%;cursor:pointer;font:inherit;color:var(--ss-text);background:var(--ss-surface-raised);border:1px solid var(--ss-border-strong);box-shadow:var(--ss-elev-pop);transition:color .15s,border-color .15s,background .15s,transform .12s;touch-action:none;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none}
    .cfab-btn svg{width:20px;height:20px;pointer-events:none}
    @media(hover:hover){.cfab-btn:hover{background:var(--ss-surface-soft)}}
    .cfab-btn:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .cfab.is-press .cfab-btn{transform:scale(.94)}
    .cfab.is-dragging .cfab-btn{cursor:grabbing;transition:none;transform:none}
    .cfab.is-on .cfab-btn,.cfab.is-on .cfab-btn:hover{color:#fff;background:var(--ss-brand);border-color:var(--ss-brand)}
    .cfab.is-live .cfab-btn,.cfab.is-live .cfab-btn:hover{color:var(--ss-danger-text);background:var(--ss-danger-tint);border-color:var(--ss-danger);transform:scale(1.06)}
    .cfab.is-live .cfab-btn::before{content:"";position:absolute;inset:-7px;border-radius:50%;border:2px solid var(--ss-danger);animation:cfabRing 1.1s ease-out infinite;pointer-events:none}
    @keyframes cfabRing{from{transform:scale(.9);opacity:.9}to{transform:scale(1.3);opacity:0}}
    .cfab-n{position:absolute;top:-5px;right:-5px;min-width:20px;height:20px;padding:0 5px;box-sizing:border-box;border-radius:10px;border:2px solid var(--ss-bg);background:var(--ss-text);color:var(--ss-bg);font:700 11px/16px system-ui,-apple-system,sans-serif;text-align:center;font-variant-numeric:tabular-nums;pointer-events:none}
    .cfab-n.is-new{background:var(--ss-danger);color:#fff}
    .cfab-n:empty{display:none}
    .cfab.is-bump .cfab-btn{animation:cfabBump .5s cubic-bezier(.3,1.6,.5,1)}
    @keyframes cfabBump{0%{transform:scale(1)}40%{transform:scale(1.14)}100%{transform:scale(1)}}
    .cfab-bub{position:fixed;z-index:39;box-sizing:border-box;width:max-content;max-width:min(300px,calc(100vw - 92px));padding:.6rem .75rem;border-radius:12px;background:var(--ss-surface-raised);color:var(--ss-text);border:1px solid var(--ss-border);box-shadow:var(--ss-elev-pop);font-size:.8rem;line-height:1.45;opacity:0;transform:translateX(10px) scale(.97);transform-origin:right bottom;transition:opacity .2s,transform .2s}
    /* The last window opened is on top. Speak's root and this button are both z 39, and
       the button comes later in the page, so it always won and sat over Speak's open
       panel (Misha, 2026-09-30). Speak's panel opening steps the button and its bubble
       under it; the button's next move brings them back over (cornerUnder). */
    .cfab.is-under,.cfab-bub.is-under{z-index:38}
    .cfab-bub.is-left{transform-origin:left bottom;transform:translateX(-10px) scale(.97)}
    .cfab-bub.on{opacity:1;transform:none}
    .cfab-bub[hidden]{display:none}
    .cfab-bub.is-tip{background:var(--ss-inverse-surface);color:var(--ss-inverse-text);border-color:transparent}
    .cfab-bub.is-voice{background:var(--ss-danger-tint);border-color:var(--ss-danger-tint-border,var(--ss-border))}
    .cfab-who{display:flex;align-items:center;gap:.45rem;min-width:0}
    .cfab-who b{font-weight:600}
    .cfab-who span{color:var(--ss-text-subtle)}
    .cfab-say{margin:.3rem 0 0;overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
    /* A pin's preview (pinPeek in viewer.ts): one card for every pin, fixed at the
       origin and moved with a transform, so showing it or following a scroll never
       lays out the page. */
    .pin-peek{position:fixed;left:0;top:0;z-index:45;width:300px;max-width:calc(100vw - 16px);padding:.65rem .75rem .6rem;border-radius:10px;background:var(--ss-surface);color:var(--ss-text);border:1px solid var(--ss-border);box-shadow:var(--ss-elev-pop);font-size:.82rem;line-height:1.45;cursor:pointer;will-change:transform;opacity:0;transition:opacity .12s ease}
    .pin-peek[hidden]{display:none}
    .pin-peek.is-on{opacity:1}
    .pin-peek-head{display:flex;align-items:center;gap:.45rem;min-width:0}
    .pin-peek-name{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
    .pin-peek-time{color:var(--ss-text-subtle);font-size:.74rem;white-space:nowrap;margin-left:auto;flex-shrink:0}
    .pin-peek-body{margin-top:.35rem;color:var(--ss-text-muted);overflow-wrap:anywhere;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
    .pin-peek-body p,.pin-peek-body ul,.pin-peek-body ol{margin:0;display:inline}
    .pin-peek-body.is-empty{font-style:italic;color:var(--ss-text-subtle)}
    .pin-peek-foot{display:flex;align-items:center;gap:.5rem;margin-top:.45rem;font-size:.74rem;color:var(--ss-text-subtle)}
    .pin-peek-state{padding:.05rem .45rem;border-radius:999px;background:var(--ss-surface-muted);color:var(--ss-text-muted);font-weight:600}
    .pin-peek-state.is-resolved{background:var(--ss-success-tint);color:var(--ss-success-text)}
    .pin-peek-open{margin-left:auto}
    @media (prefers-reduced-motion:reduce){.pin-peek{transition:none}}
    .cfab-voice{display:flex;align-items:center;gap:.4rem;font-size:.72rem;font-weight:600;color:var(--ss-danger-text)}
    .cfab-voice i{width:8px;height:8px;border-radius:50%;background:var(--ss-danger);animation:cfabBlink 1s ease-in-out infinite}
    @keyframes cfabBlink{50%{opacity:.3}}
    .cfab-acts{display:flex;gap:.4rem;margin-top:.5rem}
    .cfab-acts button{min-height:32px;padding:0 .75rem;border-radius:8px;font:inherit;font-size:.78rem;font-weight:600;cursor:pointer;border:1px solid var(--ss-border-strong);background:var(--ss-surface);color:var(--ss-text)}
    .cfab-acts button.go{border-color:var(--ss-brand);background:var(--ss-brand);color:#fff}
    .cfab-bub.is-tip .cfab-acts button{border-color:transparent;background:transparent;color:var(--ss-inverse-text-subtle);padding:0 .25rem;min-height:24px}
    @media (prefers-reduced-motion: reduce){.cfab-bub,.cfab.is-bump .cfab-btn,.cfab.is-live .cfab-btn::before{animation:none;transition:none}}
    @media print{.cfab,.cfab-bub{display:none}}
    /* A comment that arrived while you were here slides in; someone typing a reply
       says so under the thread. */
    .cmt.arrive{animation:cmtArrive .6s ease}
    @keyframes cmtArrive{from{opacity:0;transform:translateY(-6px);background:var(--ss-brand-tint)}to{opacity:1;transform:none}}
    .thread-typing{padding:.1rem .75rem .45rem;font-size:.75rem;color:var(--ss-text-subtle)}
    .thread-typing::after{content:"";display:inline-block;width:1em;text-align:left;animation:cmtDots 1.2s steps(4,end) infinite}
    @keyframes cmtDots{0%{content:""}25%{content:"."}50%{content:".."}75%{content:"..."}}
    .more-btn,.fs-btn{padding:5px 7px;line-height:0}
    .more-ov{position:fixed;inset:0;z-index:40;display:none}
    .more-ov.open{display:block}
    .more-dd{display:none;position:absolute;top:calc(100% + 8px);right:0;min-width:230px;max-width:calc(100vw - 1.5rem);background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.13);padding:.3rem;z-index:50}
    .more-dd.open{display:block}
    .mi-label{display:none}
    .share-wrap{position:relative}
    /* Click-outside backstop. The document-level listener below can't see clicks that
       land on the space canvas — that's a cross-origin iframe, and the event never
       reaches this document — so an open menu used to sit there while you clicked at
       the page underneath it. This layer covers the viewport (iframe included) and
       takes that click instead. Transparent here; the mobile sheet dims it. */
    .share-overlay{display:none;position:fixed;inset:0;background:transparent;z-index:99}
    .share-overlay.open{display:block}
    .share-btn{background:none;border:1px solid var(--ss-border-strong);color:var(--ss-text-subtle);border-radius:6px;padding:4px 10px;font-size:.8rem;cursor:pointer;display:flex;align-items:center;gap:5px;transition:border-color .15s,color .15s}
    .share-btn:hover{border-color:var(--ss-border-strong);color:var(--ss-text)}
    /* Each header has one call to action, and it wears the brand fill: Share for a
       signed-in reader, Sign in for one who isn't (it borrows .share-btn's shape). */
    #shareBtn,#signInBtn{background:var(--ss-brand);border-color:var(--ss-brand);color:var(--ss-on-brand);font-weight:600;text-decoration:none;transition:background .15s,border-color .15s}
    #shareBtn:hover,#signInBtn:hover{background:var(--ss-brand-hover);border-color:var(--ss-brand-hover);color:var(--ss-on-brand)}
    #shareBtn:focus-visible,#signInBtn:focus-visible{outline:2px solid var(--ss-focus);outline-offset:2px}
    .share-dd{display:none;position:absolute;top:calc(100% + 8px);right:0;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:16px;width:360px;max-width:calc(100vw - 24px);max-height:calc(100dvh - 72px);overflow-y:auto;overscroll-behavior:contain;padding:0;z-index:100;box-shadow:0 12px 32px rgba(0,0,0,.1)}
    .share-dd.open{display:block}
    .share-sec{padding:14px}
    .share-sec+.share-sec{border-top:1px solid var(--ss-border-soft)}
    /* An eyebrow, not a heading. The four sections needed more presence than 13px
       sentence case gave them, but the obvious fix — a bigger type size — puts the
       label above the 14px rung titles it introduces and inverts the hierarchy.
       Caps and tracking buy the presence at the same size, and read as a label. */
    .share-lbl{font-size:13px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ss-text-subtle);margin-bottom:10px}
    /* Getting the link is what most opens of this menu are for, so it leads: a
       full-width primary above the rungs, and the one place a copy is confirmed —
       picking a tier still copies, and flashes here rather than somewhere else.
       The compact sharing treatment overrides padding, type size and radius; the shared copy component still owns feedback and accessibility. The copy gesture
       itself (glyph crossfade, timing, live region, the fallback for a denied
       clipboard) is the component's throughout. */
    .share-copy-bar{padding:14px;border-bottom:1px solid var(--ss-border-soft)}
    .share-copy-link{padding:10px;border-radius:10px;font-size:14px;width:100%;justify-content:center;font-weight:var(--ss-btn-font-weight);background:var(--ss-brand);border-color:var(--ss-brand);color:var(--ss-color-on-brand)}
    .share-copy-link:hover{background:var(--ss-brand-hover);border-color:var(--ss-brand-hover);color:var(--ss-color-on-brand)}
    /* Green-on-indigo is unreadable, so the confirmed and failed states swap the
       fill out rather than only the text colour. Paired with :hover at equal
       specificity, or .ss-copy--md:hover repaints them grey mid-confirmation. */
    .share-copy-link.is-copied,.share-copy-link.is-copied:hover{background:var(--ss-success-tint);border-color:var(--ss-success-tint-border);color:var(--ss-success-text)}
    .share-copy-link.is-failed,.share-copy-link.is-failed:hover{background:var(--ss-danger-tint);border-color:var(--ss-danger-tint-border);color:var(--ss-danger-text)}
    .share-lbl-note{color:var(--ss-text-faint);text-transform:none;letter-spacing:normal;font-weight:600}
    /* Posting is an outbound act, not a setting, so it stays folded behind a CTA:
       the menu opens at the same height it always did, and the composer is one
       click away. Collapsed it's max-height 0 AND visibility:hidden — the second
       is what keeps the textarea and Post button out of the tab order. */
    .post-cta{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;background:none;border:none;padding:0;font:inherit;font-size:.82rem;color:var(--ss-text-muted);cursor:pointer;text-align:left}
    .post-cta:hover{color:var(--ss-text)}
    /* The megaphone is the same glyph the card's kebab uses for "promote this space"
       — posting to a flock is that act, so it wears the same mark. It also separates
       this row from the three settings above it: those describe who may look, this
       one goes out and tells people. */
    .post-cta-label{display:flex;align-items:center;gap:8px;min-width:0}
    .post-cta .post-cta-icon{flex:none;color:var(--ss-text-faint)}
    .post-cta:hover .post-cta-icon{color:var(--ss-text-subtle)}
    .post-cta .post-cta-chev{flex:none;color:var(--ss-text-faint);transition:transform .16s ease}
    .post-cta[aria-expanded="true"] .post-cta-chev{transform:rotate(180deg)}
    .post-body{max-height:0;opacity:0;visibility:hidden;overflow:hidden;transition:max-height .16s ease,opacity .16s ease,visibility 0s linear .16s}
    .post-body.open{max-height:340px;opacity:1;visibility:visible;transition:max-height .16s ease,opacity .16s ease,visibility 0s}
    @media (prefers-reduced-motion:reduce){.post-body,.post-body.open,.post-cta .post-cta-chev{transition:none}}
    /* Literally the card the feed will render — same component, so the preview can't
       drift from the post. */
    
    /* min-width: 0 because a card is usually a grid or flex item, whose default
       minimum is its content's width — and the title never wraps, so one long title
       used to set the whole column's width and push every card past a phone's edge
       (the profile page scrolled sideways). With it, the title ellipsizes instead. */
    .space-card { min-width: 0; display: flex; gap: 1rem; padding: 1rem 1.25rem; border: 1px solid var(--ss-border); border-radius: 10px; text-decoration: none; color: inherit; transition: border-color 0.15s, box-shadow 0.15s; background: var(--ss-surface); box-shadow: var(--ss-elev-1); }
    a.space-card:hover { border-color: var(--ss-border-strong); box-shadow: var(--ss-elev-2); }
    .space-emoji { font-size: 1.75rem; flex-shrink: 0; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; background: var(--ss-surface-muted); border-radius: 10px; }
    .space-body { flex: 1; min-width: 0; }
    /* block, not flex: text-overflow:ellipsis is ignored on a flex container, so a
       long title was chopped mid-word with no "…" — and, on a bookmarked card, chopped
       underneath the bookmark. Nothing sits beside the title, so flex bought nothing. */
    .space-title { font-weight: 500; font-size: 0.95rem; margin-bottom: 0.15rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .space-desc { font-size: 0.82rem; color: var(--ss-text-subtle); line-height: 1.4; margin-bottom: 0.3rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .space-meta { font-size: 0.75rem; color: var(--ss-text-faint); display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; min-width: 0; }
    .space-meta > * { min-width: 0; }
    .mode-tag { background: var(--ss-success-tint); color: var(--ss-success); font-size: 0.68rem; font-weight: 600; padding: 1px 5px; border-radius: 3px; }
    .mode-tag.mode-worker { background: var(--ss-brand-tint); color: var(--ss-brand-text); }
    .access-tag { display: inline-flex; align-items: center; gap: 3px; font-size: 0.68rem; font-weight: 600; padding: 1px 6px 1px 5px; border-radius: 3px; background: var(--ss-surface-muted); color: var(--ss-text-subtle); }
    .access-tag svg { flex-shrink: 0; }
    .access-tag.access-link, .access-tag.access-signedin { background: var(--ss-brand-tint); color: var(--ss-brand-text); }
    .access-tag.access-members, .access-tag.access-org, .access-tag.access-team { background: var(--ss-warn-tint); color: var(--ss-warn-text); }
    /* Same chip shape as the access tags, in the promotion accent — a card can carry
       both, and they read as one row of facts about where this space is seen. */
    /* The brand fill, which is the one strong colour on this page — a card nobody
       has opened yet is the only thing in the grid making a claim on attention. */
    .new-tag { background: var(--ss-brand); color: #fff; font-size: 0.66rem; font-weight: 700; letter-spacing: 0.03em; padding: 1px 6px; border-radius: 3px; text-transform: uppercase; }
    .featured-tag { display: inline-flex; align-items: center; gap: 3px; font-size: 0.68rem; font-weight: 600; padding: 1px 6px 1px 5px; border-radius: 3px; background: var(--ss-warn-tint); color: var(--ss-warn-text); }
    .featured-tag svg { flex-shrink: 0; }
    /* Truncate rather than wrap: a long slug used to break mid-word onto a second
       line and make one card taller than its neighbours in the grid. */
    .space-url { font-family: 'SF Mono', 'Fira Code', monospace; color: var(--ss-brand-text); font-size: 0.72rem; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .space-stat { color: var(--ss-text-subtle); }
    /* The heart and the bookmark sit over the card, not inside its link, as ONE pill in
       the top-right corner — two segments with a hairline between them, so they read
       as a pair of ways to rate the space rather than two stray icons. The pill is
       hidden until hover (or until either segment is on, or carries a tally), so a
       quiet grid stays quiet. */
    .space-card-wrap { position: relative; display: flex; }
    .space-card-wrap > .space-card { flex: 1; min-width: 0; }
    .card-acts { position: absolute; top: 8px; right: 8px; display: flex; align-items: stretch; overflow: hidden; background: var(--ss-surface); border: 1px solid var(--ss-border); border-radius: 8px; opacity: 0; transition: opacity 0.15s; }
    .space-card-wrap:hover .card-acts, .card-acts:focus-within,
    .space-card-wrap.is-fav .card-acts, .space-card-wrap.is-liked .card-acts,
    .space-card-wrap.has-count .card-acts, .space-card-wrap.has-likes .card-acts { opacity: 1; }
    .fav-btn, .like-btn { display: inline-flex; align-items: center; gap: 3px; background: none; border: none; padding: 3px 6px; line-height: 0; cursor: pointer; color: var(--ss-text-faint); transition: color 0.15s, background 0.15s; }
    /* The divider: the bookmark's left edge, whenever a heart sits before it. */
    .like-btn + .fav-btn { border-left: 1px solid var(--ss-border); }
    .fav-btn:hover { color: var(--ss-crown); background: var(--ss-surface-muted); }
    .like-btn:hover { color: var(--ss-danger-text); background: var(--ss-surface-muted); }
    .fav-btn:focus-visible, .like-btn:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: -2px; }
    .space-card-wrap.is-fav .fav-btn { color: var(--ss-crown); }
    .space-card-wrap.is-liked .like-btn { color: var(--ss-danger-text); }
    /* The tally rides inside its segment, right of the glyph, in the glyph's own
       colour — one object that says "liked, and by this many", not a badge competing
       with it. Tabular figures so 8 and 11 sit at the same width and nothing shifts. */
    .fav-count, .like-count { font-size: 0.7rem; font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: 0.01em; }
    .fav-count:empty, .like-count:empty { display: none; }
    /* The pill is positioned over the card, so the title has to stop short of it —
       otherwise a long title ellipsizes at the card edge and runs under the icons.
       The clearance is a sum: each segment adds its width, a tally adds its digits,
       and the kebab adds its own slot. */
    .space-card-wrap { --acts-bm: 0px; --acts-like: 0px; --acts-menu: 0px; }
    .space-card-wrap.has-bookmark { --acts-bm: 30px; }
    .space-card-wrap.has-bookmark.has-count { --acts-bm: 48px; }
    .space-card-wrap.has-like { --acts-like: 30px; }
    .space-card-wrap.has-like.has-likes { --acts-like: 48px; }
    .space-card-wrap.has-menu { --acts-menu: 26px; }
    .space-card-wrap .space-title { padding-right: calc(var(--acts-bm) + var(--acts-like) + var(--acts-menu) + 6px); }
    /* With a kebab beside it the pill gives up the corner and slides left. */
    .space-card-wrap.has-menu .card-acts { right: 34px; }
    /* Unlike the bookmark, the kebab is always on screen: it's the way in to everything
       else a card can do, and a control you have to hover to discover isn't one people
       find. It stays pale grey so a grid of cards is no louder than before. */
    .card-menu { position: absolute; top: 8px; right: 8px; }
    .card-menu-btn { display: inline-flex; background: none; border: none; padding: 4px; line-height: 0; cursor: pointer; color: var(--ss-text-faint); border-radius: 6px; transition: color 0.15s, background 0.15s; }
    .card-menu-btn:hover { color: var(--ss-text-subtle); background: var(--ss-surface-muted); }
    .card-menu-btn:focus-visible { outline: 2px solid var(--ss-focus); outline-offset: 1px; }
    .card-menu.open .card-menu-btn { color: var(--ss-text-subtle); background: var(--ss-surface-muted); }
    /* width:max-content + nowrap on the labels: the menu sizes to its longest entry
       instead of wrapping "Promote to Featured" onto a second line. */
    .card-menu-dd { display: none; position: absolute; top: calc(100% + 4px); right: 0; z-index: 30; width: max-content; max-width: 260px; padding: 4px; background: var(--ss-surface); border: 1px solid var(--ss-border); border-radius: 10px; box-shadow: var(--ss-elev-pop); }
    .card-menu.open .card-menu-dd { display: block; }
    .card-menu-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 9px; background: none; border: none; border-radius: 7px; font: inherit; font-size: 0.84rem; line-height: 1.2; white-space: nowrap; color: var(--ss-text-muted); text-align: left; cursor: pointer; }
    .card-menu-item:hover { background: var(--ss-surface-muted); }
    .card-menu-item svg { flex-shrink: 0; color: var(--ss-text-subtle); }
    /* Greyed but still readable, and still hoverable so its title says why. */
    .card-menu-item[disabled] { color: var(--ss-text-faint); cursor: default; }
    .card-menu-item[disabled]:hover { background: none; }
    .card-menu-item[disabled] svg { color: var(--ss-text-faint); }
    /* "Who can view": the rungs as a group, fenced off from the one-shot actions
       below it by a hairline. The check sits where the crown does on admin rows. */
    .card-menu-sec { padding-bottom: 4px; margin-bottom: 4px; border-bottom: 1px solid var(--ss-border-soft); }
    .card-menu-lbl { padding: 6px 9px 4px; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ss-text-faint); }
    .card-menu-note { margin-left: 6px; font-size: 0.74rem; color: var(--ss-text-faint); }
    .card-menu-check { margin-left: auto; padding-left: 10px; line-height: 0; visibility: hidden; }
    .card-menu-item[aria-checked="true"] .card-menu-check { visibility: visible; }
    .card-menu-item[aria-checked="true"], .card-menu-item[aria-checked="true"] svg { color: var(--ss-brand-text); }
    .card-menu-crown { margin-left: auto; line-height: 0; }
    .card-menu-crown svg { color: var(--ss-crown); }
    /* The crown keeps its colour when the entry is off — it marks who the entry is
       for, which doesn't change with whether it's available right now. */
    .card-menu-item[disabled] .card-menu-crown svg { color: var(--ss-crown); opacity: 0.5; }
    /* The archive toast. An inverted card by design (see Theming in CLAUDE.md), so
       it lifts to a raised grey in dark mode rather than vanishing into the page. */
    .ss-card-toast { position: fixed; left: 50%; bottom: 1.5rem; transform: translateX(-50%); display: none; align-items: center; gap: 0.8rem; background: var(--ss-inverse-surface); color: var(--ss-inverse-text); font-size: 0.84rem; padding: 0.6rem 0.9rem; border-radius: 10px; box-shadow: var(--ss-elev-pop); z-index: 60; }
    .ss-card-toast.on { display: flex; }
    .ss-card-toast button { font: inherit; font-weight: 600; color: var(--ss-inverse-text); background: none; border: 1px solid var(--ss-inverse-surface-hover); border-radius: 7px; padding: 0.2rem 0.6rem; cursor: pointer; }
    .ss-card-toast button:hover { background: var(--ss-inverse-surface-hover); }
    /* Touch has no hover — leave the pill permanently visible there too. */
    @media (hover: none) { .card-acts { opacity: 1; } }
    /* Compact: the card sits inside something else (a bleat, the share menu), where
       full padding would out-shout the message it belongs to. */
    .space-card-sm { gap: 0.7rem; padding: 0.6rem 0.75rem; }
    .space-card-sm .space-emoji { width: 36px; height: 36px; font-size: 1.35rem; border-radius: 8px; }
    .space-card-sm .space-title { font-size: 0.88rem; }
    .space-card-locked, .space-card-gone { color: var(--ss-text-subtle); }
    .space-card-gone { background: var(--ss-surface-muted); }
    .space-card-locked .space-title, .space-card-gone .space-title { font-weight: 500; color: var(--ss-text-subtle); }
    /* The page's own colours on its card (shared/doc-palette.ts reads them at publish,
       space-palette.ts keeps them). The card carries two custom properties as DATA —
       --sc-g its ground, --sc-a its accent — and the recipe lives here, in tokens: a
       light page gets its paper on the tile and a 10% wash of its accent on the body;
       a page that chose a dark ground gets the whole card in that ground with the
       inverse ink, lifted a quarter of the way toward the surface in light mode so it
       sits with its neighbours instead of punching a hole in the grid. A card with no
       palette has no data-tint and renders as before. The lift and the dark line are
       twinned in doc-palette.ts (DARK_LIFT, DARK_L), where a test keeps the ink AA.
       Design: design-explorations/card-tint.html, variant E. */
    .space-card[data-tint] { background: color-mix(in oklab, var(--sc-a, var(--ss-surface)) 10%, var(--ss-surface)); }
    a.space-card[data-tint]:hover { border-color: color-mix(in oklab, var(--sc-a, var(--ss-border-strong)) 45%, var(--ss-border-strong)); }
    .space-card[data-tint] .space-emoji { background: var(--sc-g, var(--ss-surface-muted)); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--sc-a, var(--ss-border)) 22%, var(--ss-border-soft)); }
    .space-card[data-ground="dark"]:not(.space-card-paddock) { --sc-lift: 24%; --sc-gl: color-mix(in oklab, var(--sc-g) calc(100% - var(--sc-lift)), var(--ss-surface)); background: var(--sc-gl); color: var(--ss-inverse-text); border-color: color-mix(in oklab, var(--sc-a, var(--sc-gl)) 30%, var(--sc-gl)); }
    a.space-card[data-ground="dark"]:not(.space-card-paddock):hover { border-color: color-mix(in oklab, var(--sc-a, var(--ss-inverse-text)) 65%, var(--sc-gl)); }
    .space-card[data-ground="dark"]:not(.space-card-paddock) .space-desc { color: color-mix(in oklab, var(--ss-inverse-text) 92%, var(--sc-gl)); }
    .space-card[data-ground="dark"]:not(.space-card-paddock) .space-meta, .space-card[data-ground="dark"]:not(.space-card-paddock) .space-stat { color: color-mix(in oklab, var(--ss-inverse-text) 88%, var(--sc-gl)); }
    .space-card[data-ground="dark"]:not(.space-card-paddock) .space-emoji { background: color-mix(in oklab, var(--sc-a, var(--ss-inverse-text)) 18%, var(--sc-gl)); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--sc-a, var(--ss-inverse-text)) 35%, var(--sc-gl)); }
    .space-card[data-ground="dark"]:not(.space-card-paddock) .space-url { color: color-mix(in oklab, var(--sc-a, var(--ss-brand-text)) 70%, var(--ss-inverse-text)); }
    .space-card[data-ground="dark"]:not(.space-card-paddock) :is(.mode-tag, .access-tag, .featured-tag) { background: color-mix(in srgb, var(--ss-inverse-text) 14%, transparent); color: var(--ss-inverse-text); }
    .space-card[data-ground="dark"]:not(.space-card-paddock) .mode-tag { color: color-mix(in oklab, var(--ss-success) 55%, var(--ss-inverse-text)); }
    /* In the dark theme the chrome is already a dark room: a dark page's card is its
       own ground, a step below the slate surface. Guarded both ways, like the tokens. */
    :root[data-theme="dark"] .space-card[data-ground="dark"]:not(.space-card-paddock) { --sc-lift: 0%; }
    @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .space-card[data-ground="dark"]:not(.space-card-paddock) { --sc-lift: 0%; } }
    /* The picture card (showThumb): the page's first screen
       on top at 16:10, cropped from the top, where the headline and the first numbers
       sit on almost every space. Until a picture exists the frame wears the page's own
       ground (--sc-g, its palette) with the emoji, so a card never shows an empty box. */
    .space-card.space-card-thumb { flex-direction: column; gap: 0; padding: 0; overflow: hidden; }
    .space-card-thumb .space-shot { position: relative; aspect-ratio: 16 / 10; overflow: hidden; background: var(--sc-g, var(--ss-surface-muted)); border-bottom: 1px solid var(--ss-border-soft); display: flex; align-items: center; justify-content: center; }
    .space-card-thumb .space-shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; }
    .space-card-thumb .space-shot-emoji { font-size: 2.6rem; line-height: 1; }
    .space-card-thumb .space-shot iframe { position: absolute; top: 0; left: 0; width: 1024px; height: 640px; border: 0; transform-origin: 0 0; pointer-events: none; background: white; }
    /* white, not a token: the live preview is the document, a lit sheet whatever the chrome's theme. */
    .space-card-thumb .space-body { padding: 0.7rem 0.85rem 0.8rem; display: flex; flex-direction: column; gap: 0.2rem; }
    .space-card-thumb .space-title { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.3; margin-bottom: 0; }
    .space-card-thumb .space-desc { margin-bottom: 0.15rem; }
    .space-card-wrap .space-card-thumb .space-title { padding-right: 0; }
    /* The bookmark pill and the kebab sit over the picture, so they carry a surface. */
    .space-card-wrap.has-thumb .card-menu-btn { background: var(--ss-surface); border: 1px solid var(--ss-border); padding: 3px; border-radius: 8px; }
    /* Over a picture the kebab is a button of its own beside the heart/bookmark pill: the
       same 24px height and 8px radius, and a 6px gap rather than touching (8 + 24 + 6). */
    .space-card-wrap.has-thumb.has-menu .card-acts { right: 38px; }
    .space-card-wrap.has-thumb .card-menu-btn:hover { background: var(--ss-surface-muted); }
    .space-card[data-ground="dark"]:not(.space-card-paddock).space-card-thumb .space-shot { border-bottom-color: color-mix(in oklab, var(--sc-a, var(--ss-inverse-text)) 25%, var(--sc-gl)); }
    /* Paddock: the picture is the card. Tiles (the default) lay the words over the
       picture's foot on a scrim; the dashboard's Tiles | Rows switch sets
       html[data-spaces-view="rows"] and the same markup becomes a list row, picture at
       the left. The scrim is --ss-caption-scrim, which follows the theme (paper in light,
       near-black in dark), so the caption carries the page's own inks in both. A Paddock
       card never takes the palette's dark-ground recipe (the :not() on those rules): in
       either view it is the mild tint, light in light and dark in dark.
       Design: design-explorations/dashboard-thumbnails-B.reference.html. */
    .space-card-paddock { position: relative; aspect-ratio: 4 / 3.2; }
    /* The tile is squarer than the 16:10 capture, so some width is cropped: keep the left,
       where a page's headline starts, rather than cutting both sides. */
    .space-card-paddock .space-shot img { object-position: left top; }
    .space-card-paddock .space-shot { position: absolute; inset: 0; aspect-ratio: auto; border-bottom: 0; }
    .space-card-paddock .space-body { position: absolute; left: 0; right: 0; bottom: 0; gap: 0.15rem; padding: 0.7rem 0.9rem 0.75rem; }
    /* The caption's inks belong to the tile alone. A row is the ordinary picture card laid
       sideways, so it keeps the card's own colours, the palette's dark-ground recipe
       included: forcing the page's greys there put dark words on a dark page's card. */
        /* The scrim is solid wherever there are words: the caption box carries it at full
       strength from its top edge, and the fade hangs ABOVE the box (::before), over the
       picture alone. Before, the fade lived inside the box and reached full strength
       2.2rem down, so the phone grid's own tighter padding put the title over the
       see-through part (2026-10-01). */
    html:not([data-spaces-view="rows"]) .space-card-paddock .space-body { background: linear-gradient(to bottom, color-mix(in srgb, var(--ss-caption-scrim) var(--ss-caption-scrim-mid), transparent), color-mix(in srgb, var(--ss-caption-scrim) var(--ss-caption-scrim-end), transparent)); }
    html:not([data-spaces-view="rows"]) .space-card-paddock .space-body::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 2.2rem; background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--ss-caption-scrim) var(--ss-caption-scrim-mid), transparent)); pointer-events: none; }
    html:not([data-spaces-view="rows"]) .space-card-paddock .space-title { font-size: 0.95rem; }
    .space-card-paddock .space-shot-emoji { margin-bottom: 3.4rem; }
    html[data-spaces-view="rows"] .space-card-paddock .space-shot-emoji { margin-bottom: 0; font-size: 1.8rem; }
    /* The facts are subtle, not faint: over the darkest page, faint would miss AA. */
    html:not([data-spaces-view="rows"]) .space-card-paddock :is(.space-meta, .space-stat) { color: var(--ss-text-subtle); }
    html:not([data-spaces-view="rows"]) .space-card-paddock .space-desc { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }
    a.space-card-paddock:hover { box-shadow: var(--ss-elev-2); }
    html[data-spaces-view="rows"] .spaces-paddock { grid-template-columns: minmax(0, 1fr); gap: 0.5rem; }
    html[data-spaces-view="rows"] .space-card-paddock { aspect-ratio: auto; flex-direction: row; align-items: center; gap: 0.9rem; padding: 0.5rem; overflow: visible; }
    /* The picture's outline is a share of the ink, not a border token: it has to show on
       any page's tint and any page's edge, and --ss-border-soft measured 1.05:1 against both.
       24% of --ss-text is ~1.7:1 in light and ~2:1 in dark (2026-10-01). */
    html[data-spaces-view="rows"] .space-card-paddock .space-shot { position: relative; inset: auto; flex: none; width: 128px; aspect-ratio: 16 / 10; border: 1px solid color-mix(in srgb, var(--ss-text) 24%, transparent); border-radius: 8px; }
    html[data-spaces-view="rows"] .space-card-paddock .space-body { position: static; flex: 1; min-width: 0; padding: 0; }
    html[data-spaces-view="rows"] .space-card-paddock .space-title { -webkit-line-clamp: 1; }
    html[data-spaces-view="rows"] .space-card-wrap .space-card-paddock .space-body { padding-right: calc(var(--acts-bm) + var(--acts-like) + var(--acts-menu) + 14px); }
    html[data-spaces-view="rows"] .space-card-paddock .space-desc { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    /* Centred by arithmetic, never a transform: a transform makes the menu its own stacking
       context, and its dropdown (z 30) then slid under the rows below it. The kebab
       and the pill are both 24px tall. */
    html[data-spaces-view="rows"] .space-card-wrap.has-thumb .card-menu { top: calc(50% - 12px); }
    html[data-spaces-view="rows"] .space-card-wrap.has-thumb .card-acts { top: calc(50% - 12px); }
    @media (max-width: 640px) {
      /* Two tiles to a phone row are ~150px wide: taller, and only the facts that fit
         under a two-line title (who can open it), so the words stay at the foot and the
         always-visible heart and kebab keep the corner to themselves. */
      html:not([data-spaces-view="rows"]) .space-card-paddock { aspect-ratio: 1 / 0.95; }
      html:not([data-spaces-view="rows"]) .space-card-paddock .space-desc { display: none; }
      html:not([data-spaces-view="rows"]) .space-card-paddock .space-meta > :not(.access-tag):not(.new-tag) { display: none; }
      /* Touch keeps the heart and bookmark on screen; over the picture they hid the page's
         headline, so on a tile they ride the caption's last row instead. */
      html:not([data-spaces-view="rows"]) .space-card-wrap.has-paddock .card-acts { top: auto; bottom: 8px; right: 8px; }
      html:not([data-spaces-view="rows"]) .space-card-wrap.has-paddock .space-meta { padding-right: calc(var(--acts-bm) + var(--acts-like) + 4px); min-height: 30px; align-content: flex-end; }
      html[data-spaces-view="rows"] .space-card-paddock .space-shot { width: 96px; }
      /* The pill would eat half a phone row's text, so it drops to the row's foot and the
         kebab keeps the top corner. */
      html[data-spaces-view="rows"] .space-card-wrap .space-card-paddock .space-body { padding-right: 2rem; padding-bottom: 1.4rem; }
      html[data-spaces-view="rows"] .space-card-wrap.has-thumb .card-menu { top: 6px; right: 6px; }
      html[data-spaces-view="rows"] .space-card-wrap.has-thumb .card-acts { top: auto; bottom: 6px; right: 6px; }
      html[data-spaces-view="rows"] .spaces.spaces-thumbs.spaces-paddock { grid-template-columns: minmax(0, 1fr); gap: 0.5rem; }
    }
    /* Compact (the switch's third view): Rows with the picture taken out and everything on
       one 34px line, like a mail client's list: emoji, title, description, then the facts
       in columns (views, who can open it, when). It is Rows plus html[data-spaces-dense],
       so every Rows rule above still holds (one column, the menu centred by arithmetic)
       and these only tighten it. An unopened new space is bold, like unread mail. The
       heart/bookmark pill and the kebab keep the room Rows reserves at the right edge.
       Design: design-explorations/spaces-compact-rows.html, variant A. */
    .space-row-emoji { display: none; }
    html[data-spaces-dense][data-spaces-view] .spaces.spaces-thumbs.spaces-paddock { gap: 0; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock { height: 34px; gap: 8px; padding: 0 8px; border: 0; border-bottom: 1px solid var(--ss-border-soft); border-radius: 0; background: transparent; box-shadow: none; }
    html[data-spaces-dense][data-spaces-view] .space-card-wrap:hover .space-card-paddock, html[data-spaces-dense][data-spaces-view] a.space-card-paddock:hover, html[data-spaces-dense][data-spaces-view] a.space-card-paddock:focus-visible { background: var(--ss-surface-muted); box-shadow: none; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-shot { display: none; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-row-emoji { display: block; flex: none; width: 22px; text-align: center; font-size: 0.95rem; line-height: 1; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock:not(:has(.space-row-emoji)) .space-body { margin-left: 30px; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-body { display: flex; flex-direction: row; align-items: center; gap: 8px; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-title { display: block; flex: 0 1 auto; max-width: 52%; white-space: nowrap; font-size: 0.85rem; line-height: 1.3; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-body:not(:has(.space-desc)) .space-title { flex: 1 1 auto; max-width: none; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-body:has(.new-tag) .space-title { font-weight: 700; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-desc { order: 2; flex: 1 1 0; min-width: 0; margin: 0; font-size: 0.8rem; line-height: 1.3; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-desc::before { content: "\2014\00a0"; color: var(--ss-text-faint); }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta { display: contents; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta > * { order: 1; flex: none; font-size: 0.75rem; white-space: nowrap; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta > .new-tag, html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta > .mode-tag, html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta > .featured-tag { font-size: 0.66rem; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta > .space-stat { order: 3; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta > .access-tag { order: 4; width: 68px; padding: 0; background: transparent; font-size: 0.72rem; font-weight: 500; }
    html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta > span:not([class]) { order: 5; width: 54px; text-align: right; color: var(--ss-text-subtle); font-variant-numeric: tabular-nums; }
    html[data-spaces-dense][data-spaces-view] .space-card-wrap.has-thumb .card-menu-btn { background: transparent; border-color: transparent; }
    html[data-spaces-dense][data-spaces-view] .space-card-wrap.has-thumb .card-menu-btn:hover, html[data-spaces-dense][data-spaces-view] .space-card-wrap.has-thumb .card-menu.open .card-menu-btn { background: var(--ss-surface); border-color: var(--ss-border); }
    @media (max-width: 640px) {
      /* A phone row is a 44px tap target holding the title, who can open it (the icon
         alone) and when. The heart/bookmark pill has no room; Tiles and Rows keep it. */
      html[data-spaces-dense][data-spaces-view] .space-card-paddock { height: 44px; }
      html[data-spaces-dense][data-spaces-view] .space-card-wrap .space-card-paddock .space-body { padding-bottom: 0; }
      html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-title { flex: 1 1 auto; max-width: none; font-size: 0.94rem; }
      html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-desc, html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta > .space-stat { display: none; }
      html[data-spaces-dense][data-spaces-view] .space-card-paddock .space-meta > .access-tag { width: auto; font-size: 0; gap: 0; }
      html[data-spaces-dense][data-spaces-view] .space-card-wrap.has-thumb .card-menu { top: calc(50% - 12px); right: 6px; }
      html[data-spaces-dense][data-spaces-view] .space-card-wrap .card-acts { display: none; }
    }

    /* Pinned to the viewport over the button it left, so it rises from a card in a
       grid, from the viewer header, and from a row in the phone's ⋯ menu alike.
       Pointer events off: it must never catch the next click. --dir is the way it
       travels: -1 floats up, 1 floats down (set by the script when the heart sits
       near the top of the viewport, as in the viewer header, where "up" would
       leave the screen before it could be seen). */
    .heart-burst { --dir: -1; position: fixed; z-index: 9999; pointer-events: none; color: #e0245e; transform-origin: 50% 50%; animation: heart-burst 900ms cubic-bezier(.2,.7,.3,1) forwards; }
    .heart-burst svg { display: block; }
    /* Scale in the first third, then coast; the zigzag is translateX swinging with
       smooth easing between stops; the fade holds until past halfway so the heart
       is seen at full size before it goes. */
    @keyframes heart-burst {
      0%   { opacity: 1;   transform: translate(0,     0)                        scale(1); }
      20%  { opacity: 1;   transform: translate(18px,  calc(var(--dir) * 30px))  scale(3.5); }
      35%  { opacity: 1;   transform: translate(-18px, calc(var(--dir) * 55px))  scale(5); }
      55%  { opacity: 1;   transform: translate(12px,  calc(var(--dir) * 80px))  scale(5); }
      75%  { opacity: .55; transform: translate(-8px,  calc(var(--dir) * 102px)) scale(5); }
      100% { opacity: 0;   transform: translate(0,     calc(var(--dir) * 120px)) scale(5); }
    }
    @media (prefers-reduced-motion: reduce) { .heart-burst { display: none; } }

    .post-preview{margin:8px 0 6px}
    #postMsg{width:100%;box-sizing:border-box;background:var(--ss-bg);border:1px solid var(--ss-border-strong);border-radius:6px;color:var(--ss-text);padding:7px 9px;font-size:.82rem;font-family:inherit;resize:none;line-height:1.4;transition:border-color .15s}
    #postMsg:focus{outline:none;border-color:var(--ss-brand)}
    .post-row{display:flex;align-items:center;gap:8px;margin-top:6px}
    .post-note{font-size:.72rem;color:var(--ss-text-subtle);min-width:0}
    .post-note.err{color:var(--ss-danger-text)}
    .post-note a{color:var(--ss-brand-text)}
    .share-toggle{display:flex;align-items:center;gap:10px;cursor:pointer;padding:2px 0}
    .share-toggle-text{font-size:.85rem;color:var(--ss-text-muted)}
    .share-toggle-sub{font-size:.72rem;color:var(--ss-text-faint);margin-top:1px}
    /* The rail the rung titles start on: 2px border + 10px padding + the 18px glyph
       + the 8px gap. Every sub-option indents to it, so a rung's extras line up
       under its title instead of under its icon. Change the glyph's size (it is set
       in TS, on the icon() call) or the row's padding and this number moves with it. */
    .vis-opts{display:flex;flex-direction:column;gap:4px;--vis-rail:38px}
    .vis-opt{display:flex;align-items:center;gap:8px;min-height:64px;cursor:pointer;padding:10px;border:2px solid transparent;background:var(--ss-bg);border-radius:10px;transition:border-color .15s,background .15s}
    .vis-opt>div{flex:1;min-width:0}
    .vis-opt:hover{border-color:var(--ss-border-strong);background:var(--ss-surface-muted)}
    .vis-opt input{appearance:none;width:22px;height:22px;margin:0;order:2;border:2px solid var(--ss-text-faint);border-radius:50%;flex-shrink:0}
    .vis-opt input:checked{border-color:var(--ss-brand);background:radial-gradient(circle,var(--ss-brand) 0 6px,transparent 7px)}
    .vis-opt input:focus-visible{outline:3px solid var(--ss-brand);outline-offset:3px}
    @media(forced-colors:active){.vis-opt input{appearance:auto}}
    /* The rung glyph rides the same left rail as the sub-option checkboxes below it,
       so the three choices and their extras read as one column. It takes the row's
       colour: subtle while the rung is merely on offer, brand once it's the one in
       effect — brand TEXT, not the fill, since it sits on the page ground.
       It pins to the TITLE, not to the row, so that a title which does wrap (a long
       org name on the invite rung) keeps its glyph beside the first line rather than
       letting it drift down beside the sub-text. The 18px box and the 18.2px first
       line share a centre, so no optical nudge is needed. */
    .vis-opt-icon{flex:none;align-self:flex-start;color:var(--ss-text-subtle)}
    .vis-opt.sel .vis-opt-icon{color:var(--ss-brand-text)}
    .vis-opt-text{font-size:14px;line-height:1.3;color:var(--ss-text);font-weight:600}
    .vis-team-pick{font:inherit;font-weight:600;color:var(--ss-brand-text);background:transparent;border:1px solid var(--ss-border-strong);border-radius:6px;padding:1px 4px;margin:0 1px;field-sizing:content;max-width:150px;cursor:pointer}
    .vis-opt-sub{font-size:12px;color:var(--ss-text-subtle);margin-top:4px;line-height:1.35}
    .vis-opt.sel{border-color:var(--ss-brand);background:var(--ss-brand-tint);box-shadow:0 3px 8px rgba(90,90,230,.07)}
    .vis-opt.disabled{opacity:.45;cursor:default}
    .vis-sub-opt{display:none;align-items:center;gap:8px;margin:0;padding:5px 12px 5px var(--vis-rail);cursor:pointer;font-size:12px;color:var(--ss-text-subtle)}
    .vis-sub-opt.on{display:flex}
    .vis-sub-opt input{width:17px;height:17px;accent-color:var(--ss-brand-text);margin:0;flex-shrink:0}
    .vis-sub-opt.disabled{opacity:.45;cursor:default}
    .vis-opt.disabled:hover{border-color:var(--ss-border);background:transparent}
    /* A reader cannot re-tier someone else's space, so they are shown the rung that
       IS in effect and nothing else. Three inert radios only advertise a choice that
       isn't theirs. (.vis-opt.sel is declared after .vis-opt:hover at the same
       specificity, so the one rung that does show keeps its selected look instead of
       reacting to a cursor that can't do anything with it.) */
    .vis-opts.locked .vis-opt:not(.sel){display:none}
    .vis-opts.locked .vis-opt{cursor:default}
    /* Every write in this section reverts its own control when the server refuses.
       A revert with no words is indistinguishable from nothing having happened,
       which is how a rate-limited rung change reads as "it won't select" — the one
       failure a person is most likely to hit here, and the one whose cause they
       cannot guess. Sits inside .vis-opts so it indents with the rungs. */
    .share-err{margin-top:10px;font-size:12px;line-height:1.35;color:var(--ss-danger-text)}
    .share-err[hidden]{display:none}
    .share-invite-link{display:inline-block;margin-top:7px;font-size:.78rem;color:var(--ss-brand-text);text-decoration:none}
    .share-invite-link:hover{text-decoration:underline}
    .sw{position:relative;display:inline-block;width:34px;height:18px;flex-shrink:0}
    .sw input{opacity:0;width:0;height:0}
    .sw .sl{position:absolute;cursor:pointer;inset:0;background:var(--ss-border-strong);border-radius:9px;transition:.2s}
    .sw .sl::before{content:"";position:absolute;height:14px;width:14px;left:2px;bottom:2px;background:var(--ss-surface);border-radius:50%;transition:.2s}
    .sw input:checked+.sl{background:var(--ss-brand)}
    .sw input:checked+.sl::before{transform:translateX(16px);background:var(--ss-surface)}
    .sw input:disabled+.sl{opacity:.4;cursor:default}
    /* Input, role and Add share one 36px line, so the three read as one control. */
    .share-input-row{display:flex;gap:6px;min-width:0;align-items:stretch}
    .share-input-row .btn,.share-input-row button{flex:none;height:36px;padding:0 14px;font-size:13px;border-radius:10px}
    .share-input-row input{flex:1 1 0;min-width:0;height:36px;box-sizing:border-box;background:var(--ss-bg);border:1px solid var(--ss-border-strong);border-radius:10px;color:var(--ss-text);padding:0 10px;font-size:13px;font-family:inherit;outline:none;transition:border-color .15s,box-shadow .15s}
    .share-input-row input::placeholder{color:var(--ss-text-faint)}
    .share-input-row input[type=search]{-webkit-appearance:none;appearance:none}
    .share-input-row input::-webkit-search-cancel-button,.share-input-row input::-webkit-search-decoration{-webkit-appearance:none;display:none}
    .share-input-row input:focus{border-color:var(--ss-brand);box-shadow:0 0 0 3px var(--ss-brand-tint)}
    .share-input-row .share-role{height:36px;box-sizing:border-box}
    /* Tall enough for five people before it scrolls; a list cut through a row's
       middle reads as a rendering glitch rather than "more below". */
    .share-emails{max-height:244px;overflow-y:auto;margin:0 -6px;padding:0 6px}
    .share-emails::-webkit-scrollbar{width:4px}
    .share-emails::-webkit-scrollbar-thumb{background:var(--ss-border-strong);border-radius:2px}
    .share-suggest{position:absolute;bottom:100%;left:0;right:0;margin-bottom:4px;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:8px;box-shadow:var(--ss-elev-pop);max-height:220px;overflow-y:auto;z-index:30}
    .share-suggest-item{display:flex;align-items:center;gap:8px;padding:7px 10px;cursor:pointer;font-size:.82rem;color:var(--ss-text-muted)}
    .share-suggest-item.sel,.share-suggest-item:hover{background:var(--ss-surface-muted)}
    .share-suggest-item .sug-name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .share-suggest-item .sug-handle{color:var(--ss-text-faint);font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .share-note{margin-top:6px;font-size:12px;line-height:1.4;color:var(--ss-text-subtle)}
    .share-note[hidden]{display:none}
    .share-suggest-item.via{opacity:.72}
    .sug-team,.team-tag{flex-shrink:0;font-size:.68rem;font-weight:600;border-radius:999px;padding:1px 7px;white-space:nowrap}
    .sug-team{margin-left:auto}
    .team-tag{margin-left:6px;font-weight:600}
    .sug-team.in,.team-tag.in{color:var(--ss-brand-text);background:var(--ss-brand-tint)}
    .sug-team.out,.team-tag.out{color:var(--ss-text-subtle);background:transparent;border:1px dashed var(--ss-border-strong)}
    /* A roster row: avatar · who · what they may do · ×. Fixed 44px so a row with
       a picker and a row with a quiet label stand the same height, and the right
       column is one width so every "Can edit" and every × lines up down the list. */
    .share-email-item{display:flex;align-items:center;gap:10px;min-height:44px;padding:4px 0;font-size:13px;color:var(--ss-text)}
    .share-email-item+.share-email-item{border-top:1px solid var(--ss-border-soft)}
    .share-av{flex:none}
    .share-av-group{background:var(--ss-surface-muted);color:var(--ss-text-subtle);display:inline-flex;align-items:center;justify-content:center}
    .share-who{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
    .share-who-name{display:flex;align-items:center;min-width:0;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.3}
    .share-who-sub{font-size:12px;color:var(--ss-text-subtle);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.3}
    .share-more{color:var(--ss-text-subtle);font-size:12px;min-height:32px}
    .share-item-ctl{flex:none;display:flex;align-items:center;justify-content:flex-end;gap:2px;min-width:0}
    .share-email-item .role-tag{font-size:12px;color:var(--ss-text-subtle);padding:0 8px}
    .share-role{flex:none;font:inherit;font-size:12px;color:var(--ss-text-muted);background:var(--ss-bg);border:1px solid var(--ss-border-strong);border-radius:10px;padding:0 6px;cursor:pointer;outline:none}
    .share-role:focus{border-color:var(--ss-brand)}
    .share-role:disabled{opacity:.6;cursor:default}
    /* In the list the picker is a word with a chevron, not a boxed field: five
       bordered selects stacked down a column was the loudest thing in the menu. It
       gains its edge on hover and focus, which is when it is being used. */
    .share-role-quiet{appearance:none;-webkit-appearance:none;height:30px;border-color:transparent;background-color:transparent;color:var(--ss-text-muted);padding:0 24px 0 8px;border-radius:8px;
      background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 7px center;transition:background-color .15s,border-color .15s}
    .share-role-quiet:hover{background-color:var(--ss-surface-muted)}
    .share-role-quiet:focus-visible{border-color:var(--ss-brand)}
    @media(forced-colors:active){.share-role-quiet{appearance:auto;-webkit-appearance:auto}}
    .share-rm{flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;background:none;border:none;color:var(--ss-text-faint);cursor:pointer;border-radius:8px;transition:color .15s,background .15s}
    .share-rm:hover{background:var(--ss-danger-tint);color:var(--ss-danger-text)}
    .share-rm:focus-visible{outline:2px solid var(--ss-focus);outline-offset:1px}
    .share-empty{font-size:.78rem;color:var(--ss-text-muted);padding:4px 0}
    .share-hint{font-size:.72rem;color:var(--ss-text-faint);margin-top:6px;line-height:1.4}
    
    .ss-count-badge{position:absolute;top:-6px;right:-6px;background:var(--ss-brand);color:var(--ss-inverse-text);border-radius:10px;padding:0 4px;font-size:.62rem;font-weight:700;min-width:16px;height:16px;line-height:16px;text-align:center;font-variant-numeric:tabular-nums;box-shadow:0 0 0 2px #f8f7f4;pointer-events:none}
/* Notifications are the one alert among them — red, not brand purple. */
.ss-count-badge.alert{background:var(--ss-danger)}
    .notif-wrap{position:relative}
    .notif-bell{position:relative;background:none;border:1px solid var(--ss-border-strong);color:var(--ss-text-subtle);border-radius:6px;padding:5px 8px;cursor:pointer;display:flex;align-items:center;transition:border-color .15s,color .15s}
    .notif-bell:hover{border-color:var(--ss-border-strong);color:var(--ss-text)}
    .notif-dd{display:none;position:absolute;top:calc(100% + 8px);right:0;width:340px;max-width:92vw;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.12);z-index:200;overflow:hidden}
    .notif-dd.open{display:block}
    /* Backdrop for the mobile sheet only. On a phone the sheet covers the page and
       a tap behind it should dismiss it, including a tap on the viewer's space
       canvas — a frame tap raises no click here and, unlike a desktop click, moves
       no focus either, so this layer is the only thing that can catch it. It stays
       display:none on desktop, where the listeners below see the click and the page
       underneath keeps taking it. Matches the viewer's share sheet. */
    .notif-ov{display:none;position:fixed;inset:0;background:rgba(0,0,0,.3);z-index:199}
    .notif-hdr{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.25rem .5rem .25rem .85rem;font-size:.82rem;font-weight:600;border-bottom:1px solid var(--ss-border-soft);color:var(--ss-text-muted)}
    .notif-close{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:44px;height:44px;padding:0;border:0;border-radius:6px;background:none;color:var(--ss-text-muted);cursor:pointer}
    .notif-close:hover{background:var(--ss-surface-soft);color:var(--ss-text)}
    .notif-close:focus-visible{outline:2px solid var(--ss-brand);outline-offset:-2px}
    .notif-list{max-height:min(120vh,840px,calc(100vh - 5rem));overflow-y:auto}
    .notif-list::-webkit-scrollbar{width:4px}.notif-list::-webkit-scrollbar-thumb{background:var(--ss-border-strong);border-radius:2px}
    .notif-empty{padding:1.75rem 1rem;text-align:center;color:var(--ss-text-faint);font-size:.82rem}
    .notif-item{display:flex;gap:.6rem;padding:.6rem .85rem;text-decoration:none;color:inherit;border-bottom:1px solid var(--ss-border-soft);transition:background .12s}
    .notif-item:last-child{border-bottom:none}
    .notif-item:hover{background:var(--ss-surface-soft)}
    .notif-item.unread{background:var(--ss-brand-tint)}
    .notif-item.unread:hover{background:var(--ss-brand-tint)}
    .notif-ic{flex-shrink:0;width:26px;height:26px;border-radius:50%;background:var(--ss-brand-tint);color:var(--ss-brand-text);display:flex;align-items:center;justify-content:center;font-size:.85rem;font-weight:700}
    .notif-txt{min-width:0;flex:1}
    .notif-line{font-size:.8rem;color:var(--ss-text-muted);line-height:1.35;word-break:break-word}
    .notif-line b{font-weight:600}
    /* The one link inside a row (an access request's document, whose row carries
       buttons and so can't be a link itself). The text keeps the line's own colour,
       which clears AA on the unread tint where brand text does not; the underline
       is what says it goes somewhere. */
    .notif-line a{color:inherit;text-decoration:underline;text-decoration-color:var(--ss-border-strong);text-underline-offset:2px}
    .notif-line a:hover{text-decoration-color:currentColor}
    .notif-ai{font-size:.58rem;font-weight:700;letter-spacing:.04em;color:var(--ss-brand-text);background:var(--ss-brand-tint);padding:0 4px;border-radius:3px;text-transform:uppercase}
    .notif-snip{font-size:.75rem;color:var(--ss-text-subtle);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .notif-meta{font-size:.68rem;color:var(--ss-text-faint);margin-top:3px}
    .notif-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
    .notif-btn{font-family:inherit;font-size:.8rem;font-weight:600;padding:4px 11px;border-radius:8px;cursor:pointer;border:1px solid var(--ss-border-strong);background:var(--ss-surface);color:var(--ss-text-muted);transition:background .12s,border-color .12s}
    .notif-btn:hover:not(:disabled){border-color:var(--ss-border-strong);background:var(--ss-surface-soft)}
    .notif-btn:disabled{opacity:.55;cursor:default}
    .notif-btn.approve{background:var(--ss-brand);border-color:var(--ss-brand);color:var(--ss-inverse-text)}
    .notif-btn.approve:hover:not(:disabled){background:var(--ss-brand-hover);border-color:var(--ss-brand-hover)}
    .notif-btn.decline{color:var(--ss-text-subtle)}
    .notif-btn.link{display:inline-flex;align-items:center;text-decoration:none}
    .notif-ask-input{flex:1;min-width:0;font-family:inherit;font-size:.8rem;padding:5px 10px;border:1px solid var(--ss-border-strong);border-radius:8px;background:var(--ss-surface);color:var(--ss-text)}
    .notif-ask-input:focus{outline:none;border-color:var(--ss-brand)}
    .notif-done{font-size:.72rem;color:var(--ss-success-text);margin-top:6px}
    .notif-toasts{position:fixed;top:14px;right:14px;display:flex;flex-direction:column;gap:8px;z-index:1000;pointer-events:none}
    .notif-toast{pointer-events:auto;display:flex;gap:.55rem;align-items:flex-start;width:320px;max-width:92vw;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:10px;box-shadow:0 6px 20px rgba(0,0,0,.14);padding:.6rem .75rem;text-decoration:none;color:inherit;transform:translateX(120%);opacity:0;transition:transform .25s ease,opacity .25s ease}
    .notif-toast.in{transform:translateX(0);opacity:1}
    .notif-toast .notif-line{font-size:.8rem}
    @media(max-width:640px){.notif-dd{position:fixed;top:auto;bottom:0;left:0;right:0;width:100%;max-width:100%;box-sizing:border-box;border-radius:14px 14px 0 0;max-height:75vh;max-height:75dvh;padding-bottom:env(safe-area-inset-bottom,0px)}.notif-dd.open{display:flex;flex-direction:column}.notif-hdr{flex-shrink:0}.notif-list{min-height:0;max-height:none;overflow-y:auto;overscroll-behavior:contain}.notif-toasts{top:8px;right:8px;left:8px}.notif-toast{width:auto}.notif-ov.open{display:block}}
  
    .ss-nav-loading{
  position:fixed;inset:0;z-index:10000;
  display:flex;align-items:center;justify-content:center;
  background:var(--ss-veil,rgba(248,247,244,.9));
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .18s ease,visibility .18s;
}
.ss-nav-loading.open{
  opacity:1;visibility:visible;pointer-events:auto
}
.ss-nav-loading-sheep{
  display:flex;align-items:center;justify-content:center;
  filter:drop-shadow(0 10px 18px var(--ss-shadow-soft,rgba(26,26,31,.1)));
}
.ss-nav-loading-logo{animation:ss-nav-sheep-fly 1.35s ease-in-out infinite}
.ss-nav-loading-logo svg{display:block;height:72px;width:auto;image-rendering:pixelated}
.ss-nav-loading-frames{width:154px;height:109px}
.ss-nav-loading-frames img{display:none;width:100%;height:100%;object-fit:contain}
.ss-nav-loading-frames img.is-active{display:block}
@keyframes ss-nav-sheep-fly{
  0%,100%{transform:translate(0,0) rotate(-3deg)}
  50%{transform:translate(8px,-12px) rotate(3deg)}
}
@keyframes ss-nav-sheep-pulse{0%,100%{opacity:1}50%{opacity:.45}}
/* Reduce Motion drops the two things that actually move — the logo's fly path, and
   the frame loop the client script already skips — and pulses the sheep in place
   instead. Frozen on frame one the overlay read as a hung screen, which on iOS
   (where Reduce Motion is a common system setting) meant every single load. */
@media (prefers-reduced-motion:reduce){
  .ss-nav-loading-logo,.ss-nav-loading-frames{
    animation:ss-nav-sheep-pulse 1.8s ease-in-out infinite
  }
}
    @view-transition{navigation:auto}
::view-transition-new(root){animation:none}
::view-transition-old(root){animation:ss-vt-hold 260ms linear both}
@keyframes ss-vt-hold{0%,30%{opacity:1}100%{opacity:0}}
@media (prefers-reduced-motion:reduce){::view-transition-old(root){animation:none;opacity:0}}
    .mention{color:var(--ss-brand-text);font-weight:600;background:rgba(90,90,230,.08);border-radius:3px;padding:0 2px}
    .mention.me{background:var(--ss-warn-tint);color:var(--ss-warn-text)}
    .mention-menu{position:absolute;bottom:calc(100% + 4px);left:0;right:0;background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:8px;box-shadow:0 -4px 16px rgba(0,0,0,.1);max-height:200px;overflow-y:auto;z-index:30}
    .mention-opt{display:flex;align-items:center;gap:8px;padding:6px 10px;cursor:pointer;font-size:.82rem}
    .mention-opt:hover,.mention-opt.active{background:var(--ss-brand-tint)}
    .mention-opt .mo-h{color:var(--ss-brand-text);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:0 1 auto}
    .mention-opt .mo-n{color:var(--ss-text-faint);font-size:.75rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:0 10 auto}
    /* Tags inside the document (mentionsShellScript): the list while an @ is typed in Edit, and the person's card over a tag. Both sit over the frame. */
    .ss-mn-menu{position:fixed;bottom:auto;right:auto;z-index:85;box-shadow:var(--ss-elev-pop);max-height:260px}
    .ss-mn-card{position:fixed;z-index:85;box-sizing:border-box;border-radius:10px;border:1px solid var(--ss-border);background:var(--ss-surface-raised,var(--ss-surface));color:var(--ss-text);box-shadow:var(--ss-elev-pop);font-size:13px;line-height:1.4;opacity:0;transform:translateY(3px);transition:opacity .14s ease,transform .14s ease}
    .ss-mn-card.on{opacity:1;transform:none}
    .ss-mn-card[hidden]{display:none}
    @media(prefers-reduced-motion:reduce){.ss-mn-card{transition:none;transform:none}}
    .ss-mn-main{display:flex;align-items:center;gap:10px;padding:10px 12px;color:inherit;text-decoration:none;border-radius:10px}
    .ss-mn-main:focus-visible{outline:2px solid var(--ss-focus);outline-offset:-2px}
    .ss-mn-av{flex:none}
    .ss-mn-tt{display:flex;flex-direction:column;min-width:0;flex:1}
    .ss-mn-name{font-weight:650;color:var(--ss-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .ss-mn-h{font-size:12px;color:var(--ss-text-subtle);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .ss-mn-go{flex:none;font-size:12px;font-weight:600;color:var(--ss-brand-text)}
    @media(hover:hover){.ss-mn-main:hover .ss-mn-go{text-decoration:underline;text-underline-offset:2px}}
    /* Whatever headerFitScript folds into the ⋯ becomes a menu row here instead of
       a header button. Scoped to .more-dd's children, so the same elements are
       untouched while they sit in the header. */
    /* Who's reading this right now, above the actions and ruled off from them —
       it states something rather than doing something, so it isn't a menu row. */
    .more-dd .pres-wrap{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;padding:.45rem .55rem .6rem;margin-bottom:.25rem;border-bottom:1px solid var(--ss-border-soft)}
    /* Sentence case, like the action rows under it — an uppercase micro-label would
       be a second typographic register in a four-row menu. */
    .more-dd .pres-label{display:inline;font-size:.85rem;color:var(--ss-text-subtle)}
    /* The rail's overlap and hover-scale belong to a tight header cluster; in a menu
       row there is room, so the faces sit apart and stay still. */
    .more-dd .pres-av{margin-left:0}
    .more-dd .pres-av:hover{transform:none}
    .more-dd .presence{gap:.3rem}
    /* line-height because the bookmark button carries line-height:0 so its lone icon sits
       flush in the header — which collapses a text row to nothing once it is one. */
    /* text-decoration because Report is an <a> among <button>s — without it that
       one row arrives underlined and reads as a different kind of thing. */
    .more-dd .cbtn{display:flex;align-items:center;gap:.6rem;width:100%;text-align:left;background:none;border:none;border-radius:7px;padding:.5rem .55rem;font-size:.85rem;line-height:1.3;color:var(--ss-text-muted);white-space:nowrap;text-decoration:none}
    .more-dd .cbtn:hover{background:var(--ss-surface-muted);border-color:transparent}
    .more-dd svg{flex-shrink:0;color:var(--ss-text-subtle)}
    .more-dd .bm-btn.on{background:none}
    .more-dd .bm-btn.on svg{color:var(--ss-crown)}
    .more-dd .bm-btn.on .mi-label{color:var(--ss-warn-text)}
    /* In the ⋯ menu the row reads as words, so the tally moves to the far end
       instead of wedging itself between the icon and the label. */
    .more-dd .bm-btn .fav-n{margin-left:auto;color:var(--ss-text-subtle)}
    .more-dd .heart-btn.on{background:none}
    .more-dd .heart-btn.on svg{color:var(--ss-danger-text)}
    .more-dd .heart-btn.on .mi-label{color:var(--ss-danger-text)}
    .more-dd .heart-btn .like-n{margin-left:auto;color:var(--ss-text-subtle)}
    .more-dd .ver-btn.pinned{background:var(--ss-warn-tint)}
    .more-sep{height:1px;margin:.25rem .55rem;background:var(--ss-border-soft)}
    .more-sep:first-child{display:none}
    /* Download is a row of two choices, not a button: the words say what it is, the
       chips on the right are the presses. Same inset and type as the rows around it. */
    .dl-row{display:flex;align-items:center;gap:.6rem;padding:.3rem .35rem .3rem .55rem;font-size:.85rem;line-height:1.3;color:var(--ss-text-muted);white-space:nowrap}
    .dl-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
    .dl-row.is-err .dl-label{color:var(--ss-danger-text)}
    .dl-opts{display:flex;gap:.3rem;flex-shrink:0}
    .dl-opt{font:inherit;font-size:.78rem;font-weight:500;color:var(--ss-text-muted);background:var(--ss-surface);border:1px solid var(--ss-border);border-radius:6px;padding:.2rem .55rem;cursor:pointer;text-decoration:none;line-height:1.3}
    .dl-opt:hover{background:var(--ss-surface-muted);color:var(--ss-text)}
    .dl-opt:focus-visible{outline:2px solid var(--ss-brand);outline-offset:1px}
    .dl-opt[aria-busy="true"]{opacity:.6;cursor:progress}
    /* One label per row: the compact header wording steps aside for the sentence. */
    .more-dd .mi-label{display:inline;overflow:hidden;text-overflow:ellipsis}
    .more-dd .ver-label,.more-dd .sess-label{display:none}
    /* Where the document came from is a fact about it, not something to do, so it
       closes the menu as a caption under its own rule, the way who's here opens it:
       smaller, fainter, two lines that never need a tooltip to be read. */
    .more-dd .sess-btn{align-items:flex-start;margin-top:.25rem;border-top:1px solid var(--ss-border-soft);border-radius:0 0 7px 7px;padding:.55rem .55rem .4rem}
    .more-dd .sess-btn svg{width:14px;height:14px;margin-top:2px;color:var(--ss-text-faint)}
    .more-dd .sess-btn .mi-label{display:flex;flex-direction:column;gap:.1rem;min-width:0;flex:1}
    .more-dd .sess-btn .sm-t,.more-dd .sess-btn .sm-s{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .more-dd .sess-btn .sm-t{font-size:.78rem;color:var(--ss-text-subtle)}
    .more-dd .sess-btn .sm-s{font-size:.72rem;color:var(--ss-text-faint)}
    .more-dd .sess-btn .sm-s:empty{display:none}
    .more-dd .sess-btn:hover{border-top-color:var(--ss-border-soft)}
    .more-dd .sess-btn:hover .sm-t{color:var(--ss-text-muted)}
    @media(max-width:640px){
      .hdr-right{gap:.5rem}
      .cbtn{padding:6px 8px;font-size:.75rem}
      .cbtn-txt{display:none}
      /* Share is the one action that stays out of the ⋯: it keeps its desktop size
         and sits last in the header, hard against the right edge. */
      .share-wrap{margin-left:auto;flex-shrink:0}
      .share-btn{flex-shrink:0;white-space:nowrap}
      .share-btn svg{flex-shrink:0}
      /* The phone's header is shorter, and the panel takes the width it needs. */
      .ver-dd{top:52px;right:.5rem;width:auto;min-width:230px;max-width:calc(100vw - 1rem)}
      .more-dd{right:-.25rem}
      .ver-banner{padding:.5rem .75rem;gap:.4rem;font-size:.76rem;flex-wrap:wrap}
      .edit-bar{padding:.45rem .75rem;gap:.45rem;font-size:.76rem}
      .edit-bar button{padding:6px 12px}
      .sidebar{position:fixed;inset:0;width:100%!important;z-index:50;border:none;border-radius:0}
      .sidebar.hidden{display:none}
      .rz{display:none!important}
      /* The tab is a fixed pill on the left edge, and --tab-y is where the reader
         parked it (see the press-and-hold drag in the script). touch-action:none so
         the drag doesn't fight the page for the finger; no callout so iOS doesn't
         answer the hold with its own menu. */
      .chat-handle{position:fixed;left:0;top:var(--tab-y,50%);transform:translateY(-50%);width:auto;height:auto;background:none;border:none;z-index:40;touch-action:none;-webkit-touch-callout:none}
      .chat.is-running:not(.open) .chat-handle::before{display:none}
      .chat-handle::after{display:none}
      /* Held: the tab lifts so the finger can feel it has come loose. */
      .chat-handle.held .chat-flyout{transform:scale(1.08);box-shadow:3px 6px 18px rgba(0,0,0,.24);transition:transform .12s,box-shadow .12s}
      .chat-flyout{position:static;transform:none;opacity:1;pointer-events:auto;padding:12px 5px;box-shadow:2px 2px 10px rgba(0,0,0,.12)}
      .chat.open .chat-flyout{display:flex}
      /* A drawer, not a takeover: 92% of the width, sliding in from the left, with a
         dimmed strip of the page beside it — the comments drawer mirrored. A tap on
         the strip folds it away. Off screen it is hidden too, so nothing in it can
         take focus or a screen reader's attention. */
      .chat-panel{position:fixed;top:0;bottom:0;left:0;width:92%!important;z-index:60;transform:translateX(-100%);visibility:hidden;transition:transform .24s ease,visibility 0s linear .24s}
      .chat.open .chat-panel{transform:none;visibility:visible;box-shadow:var(--ss-elev-pop);transition:transform .24s ease}
      .chat-panel-inner{width:100%;min-width:0;max-width:none;right:0}
      .chat-scrim{display:block;position:fixed;inset:0;z-index:59;border:none;padding:0;cursor:pointer;background:var(--ss-scrim,rgba(0,0,0,.35));opacity:0;pointer-events:none;transition:opacity .24s ease}
      .chat.open .chat-scrim{opacity:1;pointer-events:auto}
      .chat-close{width:38px;height:38px;border-radius:10px}
      /* 92% of a phone is tight for Session | Co-shepherd, the faces, Hide tab and «.
         Nothing wraps, « never leaves the drawer, and with both tabs up the faces
         give way (the room is still in the thread). */
      .chat-hdr{padding:.6rem .5rem .6rem .75rem;gap:.35rem}
      .chat-hdr .chat-tabs{min-width:0;overflow:hidden}
      .chat-hdr .chat-tab{white-space:nowrap;padding:4px 8px}
      .chat-hdr:has(.chat-tabs:not([hidden])) .chat-faces{display:none}
      .chat-hdr:has(.chat-tabs:not([hidden])) .chat-tabs{margin-right:auto}
      @media(prefers-reduced-motion:reduce){.chat-panel,.chat.open .chat-panel,.chat-scrim{transition:none}}
      .chat-inrow textarea{font-size:16px}
      /* No position:sticky here. The header is a flex item above .sb-list, which is the
         only thing that scrolls, so it never leaves the top on its own — sticky bought
         nothing and cost a rendering bug: WebKit resolves the scrollport up the DOM
         past .sidebar (position:fixed, so it should be the containing block) to
         .main-row's overflow:hidden box, which starts one document-header down. The bar
         landed 51px below the panel it belongs to, leaving a white gap above it and
         painting over the first comment. */
      .sb-close{font-size:1.8rem;padding:4px 10px}
      /* These float over the document; scrolling to the end of one shouldn't start
         scrolling the page behind it. */
      .sb-list{-webkit-overflow-scrolling:touch;overscroll-behavior:contain}
      /* The drawer: the comments cover the page but leave a strip of it on the
         left, dimmed — a tap there closes it. Annotate slides the drawer out to
         the right (the page is yours), and a tab on the edge brings it back. */
      .sidebar{left:56px;width:auto!important;box-shadow:-8px 0 24px rgba(0,0,0,.18);transition:transform .24s ease}
      .sidebar.away{transform:translateX(100%)}
      .sb-scrim{display:block;position:fixed;top:0;bottom:0;left:0;width:56px;z-index:49;background:var(--ss-scrim,rgba(0,0,0,.35));border:none;padding:0;cursor:pointer}
      .sb-scrim[hidden]{display:none}
      .sb-tab{display:inline-flex;align-items:center;gap:4px;position:fixed;right:0;top:50%;transform:translateY(-50%);z-index:51;border:1px solid var(--ss-border);border-right:none;border-radius:999px 0 0 999px;background:var(--ss-surface);color:var(--ss-text);font:600 .8rem/1 inherit;font-family:inherit;padding:10px 12px 10px 12px;box-shadow:var(--ss-elev-pop);cursor:pointer}
      .sb-tab[hidden]{display:none}
      .sb-sw .sb-sw-l{font-size:.72rem}
      #content{margin-bottom:var(--cmp-space,0px)}
      .cmp{position:fixed;left:8px;right:8px;width:auto;max-width:none;top:auto;bottom:8px;z-index:70;border-radius:18px}
      .cmp .mention-menu{top:auto;bottom:calc(100% + 8px)}
      .cmp .compose-row textarea{font-size:16px}
      .sb-toast{position:fixed;bottom:80px;z-index:71}
      /* With the drawer open, a toast sits over its preferences row: at the bottom it
         covered the thread that had just folded into "Resolved". */
      body:has(#sidebar:not(.hidden):not(.away)) .sb-toast{top:calc(env(safe-area-inset-top,0px) + 64px);bottom:auto;left:calc(50% + 28px)}
      .cmt-actions{opacity:1}
      .share-dd{position:fixed;top:auto;bottom:0;left:0;right:0;width:100%;max-width:none;border-radius:16px 16px 0 0;max-height:80dvh;overflow-y:auto;overscroll-behavior:contain;box-shadow:0 -4px 24px rgba(0,0,0,.15)}
      .share-overlay{background:rgba(0,0,0,.3)}
      .share-dd{z-index:100}
      .compose-row textarea{font-size:16px}
      /* Inputs are 16px here so iOS doesn't zoom the page on focus; the words they
         edit sit at 15px, so opening Edit no longer jumps the text a size up. */
      .cmt-body{font-size:15px}
      .sel-quote{max-width:50vw}
    }
    /* Touch readers keep the document clear. A tap on the edge opens the panel;
       its existing vertical drag still parks the sliver wherever it is convenient.
       Hover devices retain their existing controls, including at narrow widths. */
    .more-dd .viewer-touch-action{display:none}
    @media(hover:none){
      .chat-handle{position:fixed;left:0;top:var(--tab-y,50%);transform:translateY(-50%);width:8px;height:96px;background:var(--ss-surface);border:1px solid var(--ss-border-strong);border-left:0;border-radius:0 5px 5px 0;box-sizing:border-box;z-index:40;touch-action:none;-webkit-touch-callout:none}
      .chat-handle::after{display:block;left:2px}
      .chat.is-running:not(.open) .chat-handle::before{display:block;background-size:100% 56px}
      .chat .chat-handle .chat-flyout{display:none}
      .chat.hello:not(.open) .chat-handle .chat-flyout{display:flex;width:max-content;min-width:40px;box-sizing:border-box;opacity:1;transform:translateY(-50%) translateX(0);pointer-events:auto}
      .more-dd .viewer-touch-action{display:flex}
      /* Speak's button stays in its corner on touch screens too (Misha, 2026-09-30:
         "why can't I see the microphone"); the menu row is a second way to it. */
    }
