/* ───────────────────────────────────────────────────────────────────
   The fonts live here, not on a third party.

   The type used to come from fonts.googleapis.com: a stylesheet from
   another host, in the critical path, before the first paint. Two things
   came of it. The page waited on a network round trip to another domain
   before it could draw anything, and the words painted first in the
   system fallback and then *moved* when Inter arrived — the whole page
   re-flowed one line at a time, which is the jump a reader sees when
   they open a page and again on every cold navigation.

   Self-hosted, subsetted and preloaded, the font is in the same origin
   and usually in the same millisecond as the CSS, so the words paint
   once, in the right face, and never move. `swap` stays as the net: if
   the file is slow, the text shows in the fallback rather than nothing,
   and the preload keeps that window short. */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/inter-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/inter-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/inter-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/inter-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/static/fonts/jetbrains-mono-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/static/fonts/jetbrains-mono-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/static/fonts/jetbrains-mono-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/static/fonts/jetbrains-mono-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Snaga design tokens — vanilla palette (light) */
:root {
  color-scheme: light;
  accent-color: var(--accent);
  --desk: #fafafa;
  --paper: #fff;
  --paper-tint: #f4f4f5;
  --ink: #18181b;
  --ink-soft: #52525b;
  --ink-mute: #6b6b74;
  --rule: #0000006b;
  --rule-line: #0000000d;
  --margin-line: #6366f1;
  --accent: #1d9bf0;
  --accent-strong: #1a8cd8;
  --success: #157e3c;
  --warning: #b45309;
  --danger: #d4183d;
  --user-ink: #2563eb;
  --neon-cyan: #38bdf8;
  --neon-violet: #a78bfa;
  --brand-tech-cyan: #0891b2;
  --brand-tech-violet: #7c3aed;
  --brand-tech-highlight: #f0fdfa;

  /* Categorical beat accents and breaking banner */
  --beat-war: #b42318;
  --beat-politics: #7a5af8;
  --beat-economy: #b54708;
  --beat-tech: #0e9384;
  --beat-culture: #c11574;
  --beat-sports: #175cd3;
  --beat-society: #475467;
  --breaking-bg: #b42318;
  --breaking-ink: #fff;

  --font-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono",
    Menlo, Consolas, monospace;
  --font-ui: var(--font-sans);
  --font-display: var(--font-sans);

  --text-3xs: 10px;
  --text-mono-micro: 9px;
  --text-2xs: 11px;
  --text-xs: 13px;
  --text-sm: 15px;
  --text-base: 17px;
  --text-lg: 19px;
  --text-xl: 22px;
  --text-2xl: 26px;
  --text-h1: 28px;
  --text-3xl: 32px;
  --text-4xl: 40px;
  --text-5xl: 52px;

  --leading-normal: 1.5;
  --leading-relaxed: 1.625;

  --font-weight-light: 300;
  --font-weight-normal: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;
  --font-weight-black: 900;

  --tracking-tighter: -0.04em;
  --tracking-tight: -0.02em;
  --tracking-normal: 0;
  --tracking-wide: 0.02em;
  --tracking-wider: 0.05em;
  --tracking-mid: 0.12em;
  --tracking-label: 0.18em;
  --tracking-caps-wide: 0.2em;
  --tracking-widest: 0.25em;

  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;

  /* The desk has two columns and they are the same on every page:
     600 of text, 350 of context, 24 between them. The widths used to
     be written out per page — 696 on the feed, 716 on a live story,
     780 on a material, 1040 on a report — so the text moved sideways
     and re-wrapped every time the reader moved to another page. The
     column is a property of the desk now, not of the page.

     The measure is the timeline's: 600 of text, 350 of context. A
     headline set in 600 breaks the way a headline on a timeline
     breaks — three lines instead of two — and the context column gets
     the room a list of discoveries needs, not the room a ledger of
     figures needs. */
  --col-main: 600px;
  --col-side: 350px;
  --col-gap: var(--s-5);
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 14px;
  --radius-xl: 20px;

  --duration-fast: 120ms;
  --duration-base: 220ms;
  --duration-slow: 600ms;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);

  --z-base: 1;
  --z-decor: 2;
  --z-sticky: 30;
  --z-nav: 50;
  --z-banner: 60;
  --z-overlay: 70;
  --z-voice: 80;
  --z-toast: 100;

  /* Semantic mapping */
  --bg: var(--desk);
  --surface: var(--paper);
  --surface-2: var(--paper-tint);
  --text: var(--ink);
  --muted: var(--ink-mute);
  --muted-strong: var(--ink-soft);
  --line: var(--rule-line);
  --line-strong: var(--rule);
  --accent-soft: color-mix(in srgb, var(--accent) 9%, var(--paper));
  --accent-faint: color-mix(in srgb, var(--accent) 5%, transparent);
  --on-accent: #fff;
  --warn: var(--warning);
  --ok: var(--success);
  --radius: var(--radius-lg);
  --shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 8px 24px rgba(16, 24, 40, 0.05);
}

/* Snaga design tokens — X palette (dark): pure black, as x.com. The
   surface stack is X's own greys — panels #16181c, tint #212327, borders
   #2f3336 — so the page reads as one continuous black field with quiet
   cards floating on it, the way the timeline does at x.com. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --desk: #000000;
    --paper: #16181c;
    --paper-tint: #212327;
    --ink: #e7e9ea;
    --ink-soft: #b8bcc1;
    --ink-mute: #8b98a5;
    --rule: #2f3336;
    --rule-line: #2f3336;
    --margin-line: #818cf8;
    --accent: #1d9bf0;
    --accent-strong: #4ba3e0;
    --success: #4ade80;
    --warning: #fbbf24;
    --danger: #fb7185;
    --user-ink: #7dd3fc;
    --neon-cyan: #38bdf8;
    --neon-violet: #a78bfa;
    --beat-war: #f97066;
    --beat-politics: #a78bfa;
    --beat-economy: #fdb022;
    --beat-tech: #2ed3b7;
    --beat-culture: #ee46bc;
    --beat-sports: #84adff;
    --beat-society: #98a2b3;
    --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--paper));
    --accent-faint: color-mix(in srgb, var(--accent) 7%, transparent);
    --on-accent: #fff;
    --shadow: none;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --desk: #000000;
  --paper: #16181c;
  --paper-tint: #212327;
  --ink: #e7e9ea;
  --ink-soft: #b8bcc1;
  --ink-mute: #8b98a5;
  --rule: #2f3336;
  --rule-line: #2f3336;
  --margin-line: #818cf8;
  --accent: #1d9bf0;
  --accent-strong: #4ba3e0;
  --success: #4ade80;
  --warning: #fbbf24;
  --danger: #fb7185;
  --user-ink: #7dd3fc;
  --neon-cyan: #38bdf8;
  --neon-violet: #a78bfa;
  --beat-war: #f97066;
  --beat-politics: #a78bfa;
  --beat-economy: #fdb022;
  --beat-tech: #2ed3b7;
  --beat-culture: #ee46bc;
  --beat-sports: #84adff;
  --beat-society: #98a2b3;
  --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--paper));
  --accent-faint: color-mix(in srgb, var(--accent) 7%, transparent);
  --on-accent: #fff;
  --shadow: none;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* The scrollbar is always there, whether the page needs it or not. A page
     that fit the screen used to be 10px wider than one that did not, and
     the centred column moved 5px sideways when a reader went from a short
     page to a long one — the frame jumped sideways under the eye. */
  overflow-y: scroll;
  scrollbar-gutter: stable;
}

/* A thin, quiet scrollbar: the track disappears, the thumb is the rule
   grey, and the page keeps its width for the content instead of the
   scrollbar browser default. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--rule);
  border-radius: 999px;
  border: 2px solid var(--desk);
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-mute); }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--text-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* The theme toggle flips every token at once; a short transition on
     the two colours the whole page derives from eases the flip without
     easing anything a reader is waiting for. */
  transition:
    background-color var(--duration-base) var(--ease),
    color var(--duration-base) var(--ease);
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* The frame of the page: the rail on the left (from 1100px), then the
   content. The content is the two columns of the desk and nothing more
   — 600 of text, 350 of context, 24 between them. The frame used to be
   1040 wide, which made the centre column 696: too wide for a
   headline, and the whole page wider than a screen's worth of reading
   at the size the type is set. */
.wrap { width: min(974px, calc(100% - 32px)); margin: 0 auto; }

.skip-link {
  position: fixed;
  left: 12px;
  top: -70px;
  z-index: var(--z-toast);
  background: var(--accent);
  color: var(--on-accent);
  padding: 9px 14px;
  border-radius: var(--radius-md);
  font-size: var(--text-xs);
  transition: top var(--duration-fast) var(--ease);
}

.skip-link:focus { top: 12px; text-decoration: none; }

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.topbar-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 62px;
}

/* On a phone the wordmark shares the first row with the header's own
   group — with the arrow of a detail page, which stands in front of it.
   The two belong together, so the free space goes after the wordmark
   and not between the arrow and it. */
.topbar-main .brand { margin-right: auto; }

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* The header's title group. On a section page it is the section's name
   alone; on a material's own page it is the arrow back and the
   material's name, the way a post's page names the account that posted
   it. The group is the first thing in the header on a wide screen,
   where the wordmark lives in the rail. */
.topbar-title-group {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.topbar-back {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  /* The circle hugs the name it belongs to: the arrow's own padding
     would open a gap between the road back and the thing it leads to. */
  margin-left: -8px;
  border-radius: 50%;
  color: var(--text);
  transition: background-color var(--duration-fast) var(--ease);
}
.topbar-back:hover {
  background: var(--surface-2);
  text-decoration: none;
}

.icon-button {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 0;
  /* A square button with a rounded corner is a card; a circle is a
     button. The theme toggle is the one control in the frame that has
     no words next to it, so it should read as a button at a glance.
     But it is a bare mark on the header, the way a timeline's own
     icons are: a ring and a fill around a moon would make it the
     loudest thing in the frame. */
  border-radius: 50%;
  background: none;
  color: var(--muted);
  font-size: var(--text-base);
  cursor: pointer;
  /* The global `button` rule (9px 16px, meant for CTA buttons) lands in
     here too: 32px of horizontal padding inside a 40px box squeezes the
     grid area to 8px, so the 16px icon centred in that area sat 10px off
     the middle. The box sizes itself; the padding doesn't belong. */
  padding: 0;
  transition: background-color var(--duration-fast) var(--ease),
    color var(--duration-fast) var(--ease);
}

.icon-button:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}

.nav-scroll {
  overflow-x: auto;
  scrollbar-width: none;
  border-top: 1px solid var(--line);
}

.nav-scroll::-webkit-scrollbar { display: none; }

.nav-scroll nav {
  display: flex;
  gap: 18px;
  min-width: max-content;
  padding: 8px 0 7px;
  font-size: var(--text-sm);
}

/* The topbar nav reads like the app's own: muted pills, white for the
   current section. The old accent underline was a different idiom and
   the rounded hover sat awkwardly over it. Sixteen sections fit the
   1040px wrap only at compact size — at the nav's default the last
   section ran past the edge and was cut without a scrollbar. */
.nav-scroll nav {
  gap: 2px;
  font-size: var(--text-xs);
}
.nav-scroll nav a {
  color: var(--muted);
  padding: 4px 9px;
  margin: 0;
  border: 0;
  border-radius: 999px;
  transition: color var(--duration-fast) var(--ease),
    background-color var(--duration-fast) var(--ease);
}

.nav-scroll nav a:hover {
  color: var(--text);
  background: var(--surface-2);
}
.nav-scroll nav a.active {
  color: var(--text);
  font-weight: 600;
  background: var(--surface-2);
}

.topbar {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  backdrop-filter: saturate(180%) blur(10px);
}

.topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 62px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  font-weight: 700;
  font-size: var(--text-base);
}

.brand:hover { text-decoration: none; }
.brand small { color: var(--muted); font-weight: 500; font-size: var(--text-2xs); }

.brand-mark {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: var(--text-xs);
  letter-spacing: 0.02em;
}

/* The wordmark block: no tile, the mark stands on its own — the X/Vercel
   look. The logo file is dark grey built for light pages; over the dark
   theme's pure black it disappears, so it is inverted there (the two grey
   tones survive inversion as two light tones). */
.brand-mark.glyph {
  width: 24px;
  height: 24px;
  background: none;
  color: var(--text);
  border-radius: 0;
}
.brand-mark.glyph img { display: block; }
:root[data-theme="dark"] .brand-mark.glyph img {
  filter: invert(1) brightness(1.05);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .brand-mark.glyph img {
    filter: invert(1) brightness(1.05);
  }
}

nav { display: flex; gap: 6px; font-size: var(--text-sm); }
nav a {
  color: var(--muted);
  padding: 5px 11px;
  border-radius: 999px;
  /* The hover is a pill of the surface tint, in the app's own nav idiom:
     no underline jump, just a quiet rounded field. */
  transition: color var(--duration-fast) var(--ease),
    background-color var(--duration-fast) var(--ease);
}
nav a:hover { color: var(--text); background: var(--surface-2); }

.topsearch input {
  width: 190px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text);
  border-radius: 999px;
  padding: 7px 14px;
  font: inherit;
  font-size: var(--text-xs);
  outline: none;
}

/* A focused field is a field with a ring round it: the border turns
   blue and a soft blue halo follows it. The halo is a shadow, not a
   border, so nothing about the field's size changes when the reader
   points at it. */
.topsearch input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}

.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-4);
  margin: var(--s-2) 0 var(--s-4);
  padding: 6px 0 2px;
  border-bottom: 1px solid var(--line);
}

.filter-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
}

.filter-label {
  color: var(--muted);
  font-size: var(--text-3xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  margin-right: 2px;
}

.filter {
  display: inline-block;
  padding: 4px 11px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
  font-size: var(--text-2xs);
  line-height: 1.2;
}

.filter:hover { text-decoration: none; border-color: var(--accent); color: var(--text); }
.filter.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.filter.active .filter-count { opacity: 0.9; }

.badge-format { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 35%, var(--line)); }
.badge-updated { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 35%, var(--line)); }
.badge-corrected { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, var(--line)); }

.correction-banner,
.retract-banner {
  margin: 18px 0 0;
  padding: 14px 16px;
  border-radius: 10px;
  font-size: var(--text-sm);
}

.correction-banner {
  border: 1px solid color-mix(in srgb, var(--warn) 40%, var(--line));
  background: color-mix(in srgb, var(--warn) 8%, var(--surface));
  color: var(--warn);
}

.retract-banner {
  border: 1px solid color-mix(in srgb, var(--danger) 40%, var(--line));
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
  color: var(--danger);
}

.corrections {
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

.corrections h2 {
  margin: 0 0 14px;
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
}

.correction-body { font-size: var(--text-sm); line-height: 1.7; }
.correction-body p { margin: 0 0 12px; }
.correction-body p:last-child { margin-bottom: 0; }

.updates {
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

.updates h2 {
  margin: 0 0 14px;
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
}

.update-item {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-left: 3px solid var(--ok);
  border-radius: 10px;
  padding: 16px 18px;
  margin-bottom: 12px;
}

.update-item h3 { margin: 8px 0 10px; font-size: var(--text-lg); line-height: 1.3; }
.update-body { font-size: var(--text-sm); line-height: 1.7; }
.update-body p { margin: 0 0 12px; }
.update-sources { margin: 8px 0 0; padding-left: 18px; font-size: var(--text-xs); color: var(--muted); word-break: break-all; }
.badge-new { color: var(--accent); }
.badge-selected { color: var(--warn); }
.badge-duplicate { color: var(--muted); }
.badge-error { color: var(--danger); }

.nowrap { white-space: nowrap; }

.search-form {
  display: flex;
  gap: 10px;
  margin-top: 18px;
  max-width: 620px;
}

.search-form input {
  flex: 1;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  border-radius: 10px;
  padding: 11px 14px;
  font: inherit;
  font-size: var(--text-sm);
  outline: none;
}

.search-form input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}

/* The field in the search row is a field, not a tab: it carries the text
   the reader is typing, so it needs a place to put it. A bare rule under
   the text made the row a row of tabs with one of them half-drawn, so
   the field is a field — the same pill the header's search is — and the
   button beside it says what it does, in the colour of a link. */
.search-form .search-input {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text);
  padding: 9px 16px;
  font: inherit;
  font-size: var(--text-sm);
  outline: none;
}
.search-form .search-input::placeholder { color: var(--muted); }
.search-form .search-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}

/* The submit is the one control on the page that does something, so it
   says so: a link-coloured word. An underline in this row means "you
   are in this tab", and the row is not tabs. */
.search-submit {
  border: 0;
  background: none;
  padding: 9px 0 10px;
  color: var(--accent);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  cursor: pointer;
}
.search-submit:hover { color: var(--accent-strong); }
.search-form .search-input:focus { border-bottom-color: var(--accent); }

.queue-table td { font-size: var(--text-xs); }
.queue-table td:nth-child(3) { min-width: 260px; }

/* The operations pages reuse the queue table's classes for their wide
   ledgers. The queue's 260px belongs to the queue's *title* column; an
   eleven-column ledger inherits it and the last column runs past the
   panel edge, which is where its "ВІДКИНУТО" ended up clipped. */
.ops-table td:nth-child(3) { min-width: 0; }
.ops-table .num { white-space: nowrap; }

.pulse-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
  margin-bottom: 8px;
}

.kpi {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px 14px;
}

/* A panel is already a card. A card inside a card says "widget inside
   widget"; inside a panel the numbers lie flat and the panel's own
   padding does the separating. */
.panel .kpi {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
}
.panel .kpi-accent {
  background: none;
  border-color: transparent;
}

.kpi b { display: block; font-size: var(--text-xl); letter-spacing: var(--tracking-tight); }
.kpi span { color: var(--muted); font-size: var(--text-2xs); }

.kpi-accent {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--line));
}
.kpi-accent b { color: var(--accent); }
.kpi-accent span { color: var(--muted-strong); }

.org-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

.agent-card {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 16px 18px;
}

.agent-card h3 { margin: 8px 0 6px; font-size: var(--text-lg); }
.agent-card p { margin: 4px 0; }

.subhead {
  margin: 20px 0 10px;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

/* The ledger has to fit the panel that holds it: a table that runs a
   few pixels past the panel's edge is a table whose last column is
   always half a word wide. The material column is the one that can
   give ground — its headline wraps. */
.decision-table td:nth-child(2) { min-width: 190px; }

/* The decisions ledger borrows the queue table's classes, and with them
   the queue's 260px floor on its *third* column — a floor that belongs
   to the queue's title column and to nothing here. The verdict column
   wraps; it does not need a floor of its own. */
.decision-table td:nth-child(3) { min-width: 170px; }
.decision-confidence {
  display: block;
  margin-top: 4px;
  color: var(--muted);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}

.decision-reason {
  margin-top: 4px;
  color: var(--muted);
  font-size: var(--text-2xs);
  max-width: 70ch;
}

.bar-row {
  display: grid;
  grid-template-columns: 104px 1fr 190px;
  gap: 10px;
  align-items: center;
  padding: 7px 0;
}

.bar-label {
  color: var(--muted);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}

.bar-track {
  height: 10px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  overflow: hidden;
}

.bar-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  min-width: 2px;
}

.bar-value {
  color: var(--muted);
  font-size: var(--text-2xs);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.lead-card {
  background: linear-gradient(180deg, var(--accent-soft), var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line));
  border-radius: var(--radius);
  padding: clamp(22px, 4vw, 42px);
  box-shadow: var(--shadow);
  margin-bottom: 18px;
}

.lead-card h2 {
  margin: 12px 0;
  font-size: clamp(var(--text-3xl), 3.6vw, var(--text-4xl));
  line-height: 1.14;
  letter-spacing: var(--tracking-tight);
}

.lead-card h2 a { color: var(--text); }
.lead-card h2 a:hover { color: var(--accent); text-decoration: none; }
.lead-card.card-breaking h2 a:hover { color: var(--breaking-bg); }
.lead-card .dek { font-size: var(--text-lg); }

.process-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 10px 0 18px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  color: var(--muted);
  font-size: var(--text-xs);
}

.process-step { color: var(--text); font-weight: 600; }
.process-arrow { color: var(--muted); }
.process-strip a { margin-left: auto; }

/* The breadcrumb is a path, not a set of buttons. The app's `nav a` rule
   — a padded pill, meant for the section navigation — reached it because
   the breadcrumb is a <nav> as well, so every crumb carried 11px of
   horizontal padding and the separators drifted apart: the path read as
   three unrelated buttons. The crumb overrides the nav rule: flat text,
   a tight gap, and the last step in ink. */
.breadcrumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
  margin: 0 0 14px;
  color: var(--muted);
  font-size: var(--text-xs);
}

.breadcrumbs a {
  padding: 0;
  border-radius: 0;
  color: var(--muted);
  font-weight: var(--font-weight-medium);
}

.breadcrumbs a:hover {
  color: var(--text);
  background: none;
  text-decoration: underline;
}

.breadcrumbs > span {
  color: color-mix(in srgb, var(--muted) 55%, transparent);
}

.breadcrumbs > span:last-child {
  color: var(--text);
  font-weight: var(--font-weight-medium);
}

.reading-progress {
  position: fixed;
  top: 0;
  left: 0;
  z-index: var(--z-banner);
  width: 0;
  height: 3px;
  background: var(--accent);
  transition: width var(--duration-fast) linear;
}

.article-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-4);
  font-size: var(--text-xs);
}

.copy-button {
  appearance: none;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: var(--text-xs);
  padding: 6px 11px;
  cursor: pointer;
}

.copy-button:hover { border-color: var(--accent); }

.live-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin-bottom: 12px;
  color: var(--muted);
  font-size: var(--text-xs);
}

/* The filter is a control, but it is a control in a flat page: the
   native widget paints its own grey button and its own arrow, which is
   the only light box on the black desk. The field is drawn here — a
   hairline and a small caret of the text's own colour. */
.live-controls select {
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: var(--text-xs);
  padding: 5px 26px 5px 10px;
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 15px) 52%, calc(100% - 10px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.live-controls select:hover { border-color: var(--muted-strong); }

.live-controls label { display: inline-flex; align-items: center; gap: 6px; }

.schedule-card p { margin: 4px 0; }

.home-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: var(--s-5);
  align-items: start;
}

.home-main { min-width: 0; }

.home-side {
  display: grid;
  gap: var(--s-4);
  position: sticky;
  top: 118px;
  /* The column sticks to the top of the desk, so it has to fit the
     window it sticks in: a column taller than the screen hides its own
     bottom behind the fold, and nothing scrolls it back into view. It
     scrolls inside itself instead. */
  max-height: calc(100vh - 134px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}

/* The title of a card in the right column, in x.com's own way: a name
   in bold, not a label in small caps. A card in the rail is read from a
   distance and by name, so the name is the loudest thing in it, and the
   rule under it is the card's own edge.

   The selector is spelled `.panel.side-panel h2` on purpose: the panels
   of the whole site take their titles in small caps, and a card in the
   rail has to out-rank that rule, not lose to it by order. */
.panel.side-panel h2 {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0;
  padding: 12px var(--s-4);
  font-size: var(--text-lg);
  font-weight: var(--font-weight-bold);
  text-transform: none;
  letter-spacing: var(--tracking-tight);
  color: var(--text);
  border-bottom: 1px solid var(--line);
}

.timeline { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.timeline li {
  display: grid;
  gap: 2px;
  padding-left: 12px;
  border-left: 2px solid var(--line);
}
.timeline time {
  color: var(--muted);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}
.timeline a { color: var(--text); font-size: var(--text-xs); line-height: 1.4; }
.timeline a:hover { color: var(--accent); text-decoration: none; }

.feed-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-4);
}
.feed-grid .card { display: flex; flex-direction: column; }
.feed-grid .card h2 { font-size: var(--text-xl); }
.feed-grid .card .card-foot { margin-top: auto; }

/* Typographic hierarchy. The front page has four levels of weight, taken
   from the format the Scout assigned: breaking is the biggest thing on the
   page, a briefing is a line. A grid of equal cards asks the reader to do
   the ranking; a hierarchy does it for them.

   The tiers differ in more than size: the key tier keeps the card chrome,
   the standard tier drops to a quieter surface, and the brief tier loses
   the card entirely so it reads as a line of text. Chrome is part of the
   hierarchy — a bordered box says "look here" whatever its type size.

   Every tier selector is a compound (class + class), so it outranks the
   plain `.card h2` rule further down the file regardless of order. The
   tiers are written in one block, from the biggest to the smallest, and
   the lead is listed last so a lead card keeps its size even when the
   Scout called it a briefing. */
.card--key {
  border-left-width: 4px;
}

.card.card--key h2 {
  font-size: clamp(var(--text-xl), 2.4vw, var(--text-2xl));
  line-height: 1.2;
}

.card.card--key .dek { font-size: var(--text-base); }

.card--standard {
  background: var(--surface-2);
  box-shadow: none;
}

.card.card--standard h2 {
  font-size: clamp(var(--text-lg), 1.9vw, var(--text-xl));
  line-height: 1.24;
}

.card--brief {
  padding: var(--s-4) var(--s-5);
  background: transparent;
  box-shadow: none;
  border-radius: var(--radius);
}

.card.card--brief h2 {
  font-size: var(--text-base);
  font-weight: var(--font-weight-medium);
  line-height: 1.35;
  margin: 6px 0 6px;
}

.card.card--brief .dek { font-size: var(--text-sm); margin-bottom: 10px; }
.card.card--brief .card-foot { font-size: var(--text-2xs); gap: 8px; }

.card.card--lead h2 {
  font-size: clamp(var(--text-3xl), 3.6vw, var(--text-4xl));
  line-height: 1.14;
  letter-spacing: var(--tracking-tight);
}

.card.card--lead .dek { font-size: var(--text-lg); }

/* An editorial bundle: several materials on one story, printed under one
   heading. The claim "these are the same story" is made once, in the
   heading, instead of being left to the reader to work out. */
/* A bundle is a group of posts under one heading, so it is printed like
   one: no box of its own, the same hairline rhythm as the posts around
   it, and the thread line that ties the members to the heading. */
.bundle {
  margin: 0;
  padding: 14px 12px 16px;
  border-bottom: 1px solid var(--line);
}

.bundle-head { margin-bottom: var(--s-3); }

.bundle-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: var(--font-weight-semibold);
  line-height: 1.22;
  letter-spacing: var(--tracking-tight);
}

.bundle-title a { color: var(--text); }
.bundle-title a:hover { color: var(--accent); text-decoration: none; }

.bundle-kicker {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.bundle-items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0;
}

.bundle-item {
  display: grid;
  gap: 4px;
  padding: 10px 0 10px 12px;
  border-left: 2px solid var(--rule);
}

.bundle-item:last-child { border-left-color: transparent; }

.bundle-item-headline {
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
}

.bundle-item-headline:hover { color: var(--accent); text-decoration: none; }

.bundle-item-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-size: var(--text-2xs);
}

.card, .lead-card { border-left: 3px solid var(--beat, var(--line)); }

.card-breaking {
  border-left-color: var(--breaking-bg);
  border-left-width: 4px;
}
.badge-breaking {
  background: var(--breaking-bg);
  color: var(--breaking-ink);
  border-color: var(--breaking-bg);
  font-weight: 700;
  letter-spacing: 0.04em;
}
.badge-source { opacity: 0.85; }

.live-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--success);
  vertical-align: middle;
  margin-right: 2px;
  animation: live-pulse 2s ease-in-out infinite;
}

@keyframes live-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.6; transform: scale(0.92); }
}
.beat-war { --beat: var(--beat-war); }
.beat-politics { --beat: var(--beat-politics); }
.beat-economy { --beat: var(--beat-economy); }
.beat-tech { --beat: var(--beat-tech); }
.beat-culture { --beat: var(--beat-culture); }
.beat-sports { --beat: var(--beat-sports); }
.beat-society { --beat: var(--beat-society); }
.beat-other { --beat: var(--line); }

/* The urgent story is one flat band at the top of every page
   (`.breaking-bar` below). There is no second, rounded red card on the
   front page: one fact, one shape, the same on every route. */

.ai-disclosure {
  margin: 18px 0 22px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-2);
}

.ai-disclosure summary {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  color: var(--muted);
  font-size: var(--text-xs);
  list-style: none;
}
.ai-disclosure summary::-webkit-details-marker { display: none; }
.ai-disclosure[open] summary { margin-bottom: 10px; }
.ai-disclosure-body { color: var(--muted); font-size: var(--text-xs); }
.ai-disclosure-body p { margin: 6px 0; }
.ai-disclosure-body ul { margin: 6px 0 8px; padding-left: 18px; }

/* Task-specific disclosure: what happened, who is responsible, where to check. */
.disclosure-claim {
  margin: 0 0 10px;
  color: var(--ink);
  font-size: var(--text-sm);
  line-height: 1.5;
}

.disclosure-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 8px;
  margin: 0 0 10px;
  padding: 0;
  list-style: none;
}

.disclosure-facts li {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
}

.disclosure-facts b {
  color: var(--ink);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}

.disclosure-facts span {
  color: var(--ink);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.disclosure-facts em {
  color: var(--muted);
  font-size: var(--text-2xs);
  font-style: normal;
}

.disclosure-accountability {
  margin: 8px 0;
  padding: 8px 10px;
  border-left: 3px solid var(--accent);
  background: var(--surface-2);
  color: var(--ink);
  line-height: 1.5;
}

.disclosure-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin: 8px 0 0;
  font-size: var(--text-2xs);
}

/* Confirmed facts vs. what the agents could not confirm. */
.evidence-box {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--s-3);
  margin: 0 0 22px;
}

.evidence-part {
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-2);
}

.evidence-part b {
  display: block;
  margin-bottom: 6px;
  color: var(--ink);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.evidence-part ul {
  margin: 0;
  padding-left: 18px;
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: 1.55;
}

.evidence-part li { margin-bottom: 4px; }

.evidence-part--unknown {
  border-style: dashed;
  border-color: var(--muted);
}

/* Sources come before the body: a reader can check the claims as they read. */
.sources-lead {
  margin: 0 0 20px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-2);
}

.sources-lead summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-2);
  cursor: pointer;
  font-size: var(--text-xs);
  list-style: none;
}

.sources-lead summary::-webkit-details-marker { display: none; }
.sources-lead[open] summary { margin-bottom: 8px; }

.sources-lead-label {
  color: var(--ink);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.sources-lead ul { margin: 0; padding-left: 20px; }
.sources-lead li {
  margin-bottom: 6px;
  word-break: break-all;
  font-size: var(--text-sm);
  min-width: 0;
}

/* The proof line on a card: a concrete claim, not a generic badge. */
.proof {
  color: var(--muted);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}

/* The disclosure standard, stated as a list of obligations. */
.standard-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--s-3);
  margin: var(--s-3) 0 0;
  padding: 0;
  list-style: none;
}

.standard-list li {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-2);
}

.standard-list b {
  color: var(--ink);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.standard-list span {
  color: var(--muted);
  font-size: var(--text-sm);
  line-height: 1.55;
}

/* Breaking news as an interface STATE, not a page. Telemundo's pattern:
   the header takes an urgent accent while the normal layout stays exactly
   as it was. The reader gets the signal without losing their place. */
.breaking-bar {
  background: var(--breaking-bg);
  color: var(--breaking-ink);
}

.breaking-bar-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 40px;
  padding: 6px 0;
}

.breaking-bar-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding: 3px 9px;
  border-radius: var(--radius-sm);
  background: var(--breaking-ink);
  color: var(--breaking-bg);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.breaking-bar a {
  color: var(--breaking-ink);
  font-weight: var(--font-weight-semibold);
  font-size: var(--text-sm);
  text-decoration: none;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.breaking-bar a:hover { text-decoration: underline; }

.breaking-bar time {
  margin-left: auto;
  flex: 0 0 auto;
  color: rgba(255, 255, 255, 0.8);
  font-size: var(--text-2xs);
}

/* The accent reaches the top of the page too, so the state is unmistakable
   even when the reader has scrolled past the bar. */
.body-breaking .topbar {
  border-top: 3px solid var(--breaking-bg);
}
/* The brand row keeps no tile of its own, not even in breaking state: the
   glyph stands for the wordmark the way the X header does, and the red
   ribbon above already carries the state. A tile behind the mark read as
   a patch left over from the old "AI" chip. */

@media (max-width: 620px) {
  /* On a phone the ribbon keeps two fixed rows — the state and the time,
     then the headline. A wrapped headline made the bar as tall as its text,
     and every new breaking story moved the whole page under it. */
  .breaking-bar-row {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 4px var(--s-3);
  }
  .breaking-bar-label { grid-area: 1 / 1; }
  .breaking-bar time { grid-area: 1 / 2; margin-left: 0; text-align: right; }
  .breaking-bar a {
    grid-area: 2 / 1 / 3 / -1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* The live-blog index in the homepage sidebar. */
.liveblog-index {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-3);
}

.liveblog-index li {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line);
}

.liveblog-index li:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}

.liveblog-index a {
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  text-decoration: none;
}

.liveblog-index a:hover {
  color: var(--accent);
  text-decoration: underline;
}

.liveblog-index .muted {
  font-size: var(--text-2xs);
}

/* Signed provenance: the manifest a third party can verify. */
.code-block {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 460px;
}

.verify-steps {
  margin: 0 0 var(--s-3);
  padding-left: 20px;
  font-size: var(--text-sm);
  line-height: 1.65;
  color: var(--text);
}

.verify-steps li { margin-bottom: 6px; }
.verify-steps li:last-child { margin-bottom: 0; }

.kv {
  margin: 0;
  display: grid;
  gap: var(--s-3);
}

.kv dt {
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
}

.kv dd {
  margin: 4px 0 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Live blog: a developing story is a stream, not a page. The newest update
   sits at the top of the main column; "what we know" stays in a sticky
   sidebar so the reader never has to scroll past a long summary to reach
   the latest update. */
.liveblog-head {
  margin-bottom: var(--s-4);
}

.liveblog-head h1 {
  margin: var(--s-2) 0 var(--s-2);
  font-size: var(--text-2xl);
  line-height: 1.15;
  letter-spacing: var(--tracking-tight);
}

.liveblog-head .article-dek {
  font-size: var(--text-base);
  color: var(--muted-strong);
  margin: 0 0 var(--s-3);
  /* The caption row under it carries the rule; two rules in a row
     would draw the page's head twice. */
  padding-bottom: 0;
  border-bottom: 0;
}

.liveblog-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 300px);
  gap: var(--s-5);
  align-items: start;
}

.liveblog-main { min-width: 0; }

.liveblog-side {
  display: grid;
  gap: var(--s-3);
  position: sticky;
  top: calc(var(--topbar-h, 96px) + var(--s-3));
}

.liveblog-panel {
  padding: var(--s-3) var(--s-4);
}

.liveblog-panel h2 {
  margin: 0 0 var(--s-2);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
}

.liveblog-panel--unknown {
  border-style: dashed;
  border-color: var(--rule);
}

.liveblog-panel--unknown > summary {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  cursor: pointer;
  list-style: none;
}

.liveblog-panel--unknown > summary::-webkit-details-marker { display: none; }

.liveblog-panel--unknown > summary h2 {
  margin: 0;
  display: inline;
}

.liveblog-panel--unknown > summary .muted {
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}

/* A collapsed disclosure is a row you can open, not an empty box: the
   chevron says so, and it turns when the box opens. */
.liveblog-panel--unknown > summary::after {
  content: "›";
  margin-left: auto;
  color: var(--muted);
  font-size: var(--text-lg);
  line-height: 1;
  transition: transform var(--duration-fast) var(--ease);
}

.liveblog-panel--unknown[open] > summary::after {
  transform: rotate(90deg);
}

.liveblog-facts {
  margin: 0;
  padding-left: 18px;
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text);
}

.liveblog-facts li { margin-bottom: 6px; }
.liveblog-facts li:last-child { margin-bottom: 0; }

.liveblog-stream {
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
}

/* The stream of updates — the live story and the updates under a
   material — is a sequence of posts, not a stack of cards: the posts
   are divided by a hairline and the newest one keeps the accent rule.
   The rules themselves live in stream.css, which is loaded after this
   sheet. */

@media (max-width: 860px) {
  .liveblog-layout { grid-template-columns: minmax(0, 1fr); }
  .liveblog-side { position: static; }
  .liveblog-head h1 { font-size: var(--text-xl); }
}

.related {
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}
/* Only the section's own heading. A post inside the section carries a
   headline of its own, and that headline is the feed's headline — it
   must not be turned into a section label by a rule meant for the
   label. */
.related > h2 {
  margin: 0 0 12px;
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
}
.related-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
.related-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
}
.related-card:hover { border-color: var(--accent); text-decoration: none; }
.related-card h3 {
  margin: 2px 0;
  font-family: var(--font-display);
  font-size: var(--text-base);
  line-height: 1.35;
}
.related-card p { margin: 0; font-size: var(--text-xs); }

.mobile-tabbar { display: none; }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

::selection {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
}

h1, h2, h3 { text-wrap: balance; }

[id] { scroll-margin-top: 132px; }

.article-body a,
.sources a {
  text-underline-offset: 3px;
}

.button {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  border: 1px solid transparent;
  /* A button on a timeline is a pill. A rounded rectangle reads as a
     card, and the frame has no cards. */
  border-radius: 999px;
  padding: var(--s-2) var(--s-4);
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--font-weight-semibold);
  line-height: 1.2;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease),
    border-color var(--duration-fast) var(--ease);
}

.button:hover { background: var(--accent-strong); }

.button-secondary {
  background: var(--surface);
  border-color: var(--line-strong);
  color: var(--text);
}

.button-secondary:hover {
  background: var(--surface-2);
  border-color: var(--accent);
}

.button-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--accent);
}

.button-ghost:hover { background: var(--accent-soft); }

.button:disabled { opacity: 0.5; cursor: not-allowed; }

.share-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-4);
  font-size: var(--text-xs);
  color: var(--muted);
}

.share-label {
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  font-size: var(--text-2xs);
}

.share-button {
  display: inline-flex;
  align-items: center;
  padding: var(--s-1) var(--s-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text);
  font-size: var(--text-xs);
  line-height: 1.3;
}

.share-button:hover {
  border-color: var(--accent);
  color: var(--accent);
  text-decoration: none;
}

.skeleton-card {
  min-height: 118px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: linear-gradient(
    90deg,
    var(--surface-2) 25%,
    var(--surface) 37%,
    var(--surface-2) 63%
  );
  background-size: 400% 100%;
  animation: skeleton-shimmer 1.4s ease infinite;
}

@keyframes skeleton-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

.pipeline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin-top: var(--s-4);
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  font-size: var(--text-xs);
  color: var(--muted);
}
.pipeline-step {
  font-weight: 600;
  color: var(--text);
  letter-spacing: 0.01em;
}
.pipeline-step--publish { color: var(--accent); }
.pipeline-arrow { color: var(--muted); font-weight: 400; }
.pipeline-meta {
  margin-left: auto;
  color: var(--muted);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}
@media (max-width: 760px) {
  .pipeline-meta { flex-basis: 100%; margin-left: 0; }
}

.audit-steps {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  margin-top: var(--s-2);
}
.audit-step {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-2);
}
.audit-step .badge { flex-shrink: 0; }

.autonomy-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-3);
}
.autonomy-item {
  padding: var(--s-4);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-md);
  background: var(--surface-2);
}
.autonomy-item b {
  display: block;
  margin-bottom: 6px;
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--text);
}
.autonomy-item p { margin: 0; font-size: var(--text-xs); }
.autonomy-item code {
  padding: 1px 5px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: var(--text-2xs);
}

@media (max-width: 760px) {
  .autonomy-grid { grid-template-columns: 1fr; }
}

.activity {
  margin: 0 0 var(--s-5);
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow);
}

.activity-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-3);
}
.activity-head h2 {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
}
.activity-head a { font-size: var(--text-2xs); }

.activity-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 4px;
}
.activity-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  font-size: var(--text-xs);
  color: var(--muted);
  padding: 3px 0;
}
.activity-list time {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: var(--text-2xs);
  flex-shrink: 0;
}
.activity-list .badge { flex-shrink: 0; }
.activity-text { color: var(--text); }

.activity-list--compact li { padding: 2px 0; font-size: var(--text-2xs); }
.activity-list--compact .activity-text { font-size: var(--text-2xs); line-height: 1.35; }
.side-panel--live { border-color: color-mix(in srgb, var(--success) 35%, var(--line)); }
.side-panel--live h2 { color: var(--text); }

/* The body of a card in the right column: quiet text, one rhythm, and
   a link that says where it leads. */
.side-body {
  padding: var(--s-3) var(--s-4);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
  color: var(--muted-strong);
}
.side-body > :first-child { margin-top: 0; }
.side-body > :last-child { margin-bottom: 0; }
.side-body p { margin: 0 0 6px; }
.side-body a { color: var(--text); text-decoration: none; }
.side-body a:hover { text-decoration: underline; }

/* The foot of a card: one line under a rule, the way x.com closes a
   card with a single way further. */
.side-foot {
  margin: 0;
  padding: 10px var(--s-4);
  border-top: 1px solid var(--line);
  font-size: var(--text-sm);
}
.side-foot a { color: var(--accent); text-decoration: none; }
.side-foot a:hover { text-decoration: underline; }

/* Real-time ticker — the freshest headlines, always on screen. */
.ticker {
  position: sticky;
  top: var(--topbar-h, 96px);
  z-index: var(--z-sticky);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.ticker-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 42px;
  padding: 6px 0;
}

.ticker-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  color: var(--accent);
  font-size: var(--text-3xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  padding-right: var(--s-3);
  border-right: 1px solid var(--line);
}

.ticker-viewport {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  mask-image: linear-gradient(90deg, #000 calc(100% - 56px), transparent);
}

/* A slow, continuous scroll: the strip never cuts a headline in half at the
   left edge because the whole track slides, it does not tile visibly. */
.ticker-marquee {
  display: flex;
  width: max-content;
  animation: ticker-scroll var(--ticker-duration, 90s) linear infinite;
  /* Hold still first, so the freshest headline lands whole and readable. */
  animation-delay: var(--ticker-delay, 3s);
}

.ticker-viewport:hover .ticker-marquee,
.ticker-viewport:focus-within .ticker-marquee {
  animation-play-state: paused;
}

@keyframes ticker-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

.ticker-track {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: var(--s-5);
  align-items: center;
}

/* The first item starts flush with the label, not clipped. */
.ticker-track > li:first-child { padding-left: 2px; }

.ticker-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
  color: var(--text);
  font-size: var(--text-xs);
}
.ticker-item:hover { color: var(--accent); text-decoration: none; }

.ticker-age {
  color: var(--muted);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

.ticker-headline {
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 46ch;
}

.ticker-tag {
  padding: 1px 7px;
  border-radius: var(--radius-sm);
  background: var(--breaking-bg);
  color: var(--breaking-ink);
  font-size: var(--text-3xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-wide);
  flex-shrink: 0;
}

.ticker-next {
  flex-shrink: 0;
  color: var(--muted);
  font-size: var(--text-2xs);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.ticker-next b {
  color: var(--accent);
  /* The clock keeps its width: a countdown that grew and shrank with every
     tick would nudge the strip sideways once a minute. */
  display: inline-block;
  min-width: 5ch;
  text-align: right;
}

/* The strip keeps its height even when there is nothing to say, so the
   frame does not move when the news goes quiet. */
.ticker-empty {
  margin: 0;
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: 1.5;
}

@media (prefers-reduced-motion: reduce) {
  .ticker-marquee {
    animation: none;
    width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .ticker-marquee::-webkit-scrollbar { display: none; }
  .ticker-track { flex-wrap: nowrap; }
}

/* The newsroom's own pinned post.

   The home page used to open with a masthead: a display headline, a
   paragraph and a row of counters, laid out like the front page of a
   newspaper. A timeline has one kind of entry — the post — and the
   newsroom is one of the accounts posting into it, so the statement
   became a post: the same name row, the same headline, the same quiet
   bar of facts, in the same column as the news it introduces.

   The counters are that bar. A bar of counts on a timeline is what the
   account has done, said in the same quiet voice as everything else in
   the row — so the newsroom's numbers are read the way a timeline's
   numbers are read, not as a dashboard of figures over a rule. */
.post--pinned .post-title {
  font-size: clamp(22px, 2.2vw, 28px);
  line-height: 1.2;
}
.post--pinned .post-dek { font-size: var(--text-sm); }

/* The brand's own avatar is its mark, the way an account's avatar is
   its picture. A letter would read as another source's initial. */
.post-avatar--brand {
  background: color-mix(in srgb, var(--accent) 18%, var(--surface-2));
}
.post-avatar--brand img {
  width: 24px;
  height: 24px;
  display: block;
}
/* The mark is dark ink. On the dark theme it vanished into its own disc —
   the rail already inverts it; the post avatar and the chat's empty state
   were the two places that did not. */
:root[data-theme="dark"] .post-avatar--brand img,
:root[data-theme="dark"] .chat-empty-mark img { filter: invert(1) brightness(1.05); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .post-avatar--brand img,
  :root:not([data-theme="light"]) .chat-empty-mark img { filter: invert(1) brightness(1.05); }
}

/* A card that just arrived — brief accent pulse, then it settles. */
.card--fresh {
  animation: fresh-in var(--duration-slow) var(--ease) both;
  border-color: var(--accent);
}

@keyframes fresh-in {
  from { opacity: 0; transform: translateY(-10px); box-shadow: 0 0 0 4px var(--accent-faint); }
  to { opacity: 1; transform: translateY(0); box-shadow: var(--shadow); }
}

/* Toast that offers to reveal a freshly published article. */
.new-flash {
  position: fixed;
  right: var(--s-5);
  bottom: var(--s-5);
  z-index: var(--z-toast);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: 10px 16px;
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--line));
  border-radius: 999px;
  background: var(--surface);
  box-shadow: 0 8px 28px rgba(16, 24, 40, 0.14);
  font-size: var(--text-xs);
  transform: translateY(140%);
  opacity: 0;
  transition: transform var(--duration-base) var(--ease),
    opacity var(--duration-base) var(--ease);
}
.new-flash--visible { transform: translateY(0); opacity: 1; }
.new-flash a { font-weight: var(--font-weight-semibold); }

.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
  font-size: var(--text-xs);
}

.page-link {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 6px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
}
.page-link:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.page-link.disabled {
  opacity: 0.4;
  pointer-events: none;
  color: var(--muted);
}

.page-state {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  text-align: center;
  flex: 1 1 auto;
}

.badge-trigger {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--line));
  cursor: help;
}

.cycle-card .card-meta { gap: 6px 8px; }

.cycle-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 4px 0 12px;
}

.cycle-items {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: grid;
  gap: 6px;
  font-size: var(--text-xs);
}
.cycle-items li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-2);
}
.cycle-items a { color: var(--text); font-weight: 600; }
.cycle-items a:hover { color: var(--accent); text-decoration: none; }
.cycle-items .badge { flex-shrink: 0; }

.cycle-error {
  margin: 0 0 12px;
  font-size: var(--text-2xs);
  color: var(--danger);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--line));
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--danger) 6%, var(--surface));
  overflow-wrap: anywhere;
}
.cycle-errors {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: grid;
  gap: 6px;
  font-size: var(--text-2xs);
  color: var(--danger);
}
.cycle-errors li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--line));
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--danger) 6%, var(--surface));
  overflow-wrap: anywhere;
}

.timeline-item { position: relative; }
.timeline-item.timeline-breaking { border-left-color: var(--breaking-bg) !important; }
.timeline-dot {
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--breaking-bg);
  vertical-align: middle;
  margin-right: 2px;
}
.feed { animation: feed-enter var(--duration-slow) var(--ease) both; }
@keyframes feed-enter {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .topbar,
  .notice,
  .mobile-tabbar,
  .footer,
  .article-actions,
  .share-row,
  .related,
  .skip-link {
    display: none !important;
  }

  body {
    background: #fff;
    color: #000;
    font-size: 12pt;
  }

  .article {
    border: 0;
    box-shadow: none;
    padding: 0;
  }

  a { color: #000; text-decoration: underline; }
}

.company-links {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 18px;
  font-size: var(--text-sm);
}

@media (max-width: 760px) {
  .pulse-grid { grid-template-columns: repeat(2, 1fr); }
  .org-grid { grid-template-columns: 1fr; }
  .bar-row { grid-template-columns: 1fr; gap: 4px; }
  .bar-value { text-align: left; }
}

.notice {
  background: var(--accent-soft);
  color: var(--text);
  font-size: var(--text-xs);
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}

.notice-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}

.next-update {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.next-update b {
  color: var(--accent);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

main { padding: var(--s-6) 0 var(--s-7); }

.hero {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: var(--s-6);
  align-items: end;
  padding: var(--s-3) 0 var(--s-6);
}

.hero h1 {
  margin: 6px 0 10px;
  font-family: var(--font-display);
  font-size: clamp(var(--text-3xl), 4vw, var(--text-5xl));
  font-weight: var(--font-weight-semibold);
  line-height: 1.1;
  letter-spacing: var(--tracking-tight);
}

.hero-small h1 { font-size: clamp(var(--text-2xl), 3.4vw, var(--text-3xl)); }

/* A reader page opens with what it is, not with a display size. The
   pages that are lists — a search, an index of the stories that are
   still developing, a page that is not there — used to carry a 32-pixel
   headline and a lede under it, the way a landing page does. A timeline
   names the timeline it is showing and then shows it, so the name here
   is a label above the list: the same size as a headline, one step
   down from a poster, with the lede under it as a quiet sentence. */
.hero--label {
  grid-template-columns: 1fr;
  align-items: start;
  padding: 0 0 var(--s-4);
}
.hero--label h1 {
  margin: 0 0 4px;
  font-size: var(--text-xl);
  font-weight: var(--font-weight-bold);
  line-height: 1.25;
}
.hero--label .lede {
  font-size: var(--text-xs);
  line-height: 1.55;
  max-width: 76ch;
}

.eyebrow {
  margin: 0;
  color: var(--accent);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-mid);
}

.lede { margin: 0; color: var(--muted); max-width: 62ch; }

.stat-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-4) var(--s-5);
  align-items: start;
}

/* The numbers are facts, not tiles. A box around each one says
   "dashboard widget"; a timeline says "here are the counts". The masthead
   already prints them flat — this makes every page speak the same way. */
.stat {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}

.stat b { display: block; font-size: var(--text-xl); letter-spacing: var(--tracking-tight); }
.stat span { color: var(--muted); font-size: var(--text-2xs); }
.stat-wide { grid-column: 1 / -1; }

.feed { display: grid; gap: var(--s-4); }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--s-5);
  /* The timeline card does not lift or glow under the cursor — it tints.
     The lift-and-glow was a different product's idiom. */
  transition: background-color var(--duration-fast) var(--ease),
    border-color var(--duration-fast) var(--ease);
  /* A grid item's default minimum size is its content's minimum size,
     so one wide child used to widen the whole column and the page. */
  min-width: 0;
}

.card:hover {
  border-color: var(--rule);
  background: color-mix(in srgb, var(--ink) 3%, var(--surface));
}

.card h2 {
  margin: 10px 0 8px;
  font-family: var(--font-display);
  font-weight: var(--font-weight-semibold);
  font-size: clamp(var(--text-xl), 2.4vw, var(--text-2xl));
  line-height: 1.22;
  letter-spacing: var(--tracking-tight);
}

.card h2 a { color: var(--text); }
.card h2 a:hover { color: var(--accent); text-decoration: none; }

.card-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-size: var(--text-xs);
}

.badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: var(--text-2xs);
  white-space: nowrap;
  /* A badge that holds prose must not be able to push its card wider
     than the page. The nowrap above is what a *label* wants — a stage
     name, a severity, a verdict — and the correction's reason was a
     paragraph of the auditor's prose in that slot, so the page grew a
     horizontal scrollbar for 600 characters of text. The overflow
     rule below is the one that has to hold for a badge of any width. */
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* A badge that holds a sentence instead of a label. It wraps, and it
   stops being a pill — a rounded blob around four lines of text reads
   as a mistake. */
.badge-text {
  white-space: normal;
  border-radius: var(--radius-md);
  line-height: 1.5;
  text-align: left;
  padding: 5px 10px;
}

.badge-low, .badge-minimal, .badge-completed, .badge-published {
  color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 35%, var(--line));
}

.badge-medium, .badge-warn { color: var(--warn); }
.badge-high, .badge-rejected, .badge-error, .badge-failed { color: var(--danger); }

.ai-tag {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: var(--text-2xs);
  font-weight: 650;
  letter-spacing: 0.02em;
}

/* In the action bar the disclosure is a word, not a badge. A filled pill
   in a row of quiet counts is the one thing that makes the bar shout —
   and the reader still has to see that the text was machine-written. */
.post-tail .ai-tag,
.post-actions .ai-tag,
.log-actions .ai-tag {
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--muted-strong);
  font-weight: var(--font-weight-medium);
}

.dek { margin: 0 0 14px; color: var(--muted); max-width: 80ch; }

.card-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  font-size: var(--text-xs);
  color: var(--muted);
}

.card-foot .read { margin-left: auto; font-weight: 600; }
.tags { color: var(--muted); }

/* The article is a document, not a widget. On a timeline a post's own
   page is one flat column of type with nothing boxed around it, and the
   rounded panel here was a leftover from the card grid the feed used to
   be. The audit blocks below the text keep their frames: they are
   evidence, not decoration. */
.article {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
}

.article h1 {
  margin: 14px 0 12px;
  font-family: var(--font-display);
  font-weight: var(--font-weight-semibold);
  /* A headline that fills five lines of a reading column is a poster,
     not a headline. The measure stays; the shouting stops. */
  font-size: clamp(var(--text-2xl), 2.6vw, var(--text-4xl));
  line-height: 1.12;
  letter-spacing: var(--tracking-tight);
}

.article-dek {
  margin: 0 0 26px;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--line);
  color: var(--muted-strong);
  font-size: var(--text-lg);
  line-height: 1.55;
}

.article-body {
  font-size: var(--text-lg);
  line-height: 1.75;
  max-width: 68ch;
}
.article-body p { margin: 0 0 18px; }

/* A legal document is read, not skimmed: the same measure as an article,
   one size down (it is reference text, not the day's news), and headings
   that start a section rather than a paragraph. */
.legal-body {
  max-width: 68ch;
  font-size: var(--text-base);
  line-height: 1.72;
  color: var(--muted-strong);
}
.legal-body h2 {
  margin: 30px 0 8px;
  font-size: var(--text-lg);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-tight);
  color: var(--text);
}
.legal-body h2:first-child { margin-top: 4px; }
.legal-body p { margin: 0 0 14px; }
.legal-body ul { margin: 0 0 14px; padding-left: 20px; }
.legal-body li { margin-bottom: 6px; }
.legal-body strong { color: var(--text); }
.legal-body a {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.legal-body a:hover { color: var(--accent); }
.legal-body code {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

.audit {
  margin-top: 28px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-2);
}
.audit summary {
  cursor: pointer;
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
  list-style: none;
}
.audit summary::-webkit-details-marker { display: none; }
.audit[open] summary { margin-bottom: 12px; }

.sources, .verifier {
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

.sources h2, .verifier h2, .audit h2, .panel h2 {
  margin: 0 0 12px;
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--muted);
}

.sources ul { margin: 0; padding-left: 20px; }
.sources li {
  margin-bottom: 6px;
  word-break: break-all;
  font-size: var(--text-sm);
  min-width: 0;
}
.source-item { display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-2); align-items: baseline; }
.source-item a { font-weight: var(--font-weight-semibold); }
/* A source that is a citation in words, not an address: it is written in
   the same hand as a link but without the link, because there is nowhere
   to go. The dot before it says the same thing. */
.source-label {
  color: var(--muted-strong);
  font-weight: var(--font-weight-medium);
  overflow-wrap: anywhere;
}
.source-url {
  color: var(--muted);
  font-size: var(--text-2xs);
  word-break: break-all;
  /* A flex child defaults to min-width:auto, which lets a long URL push the
     row past the viewport instead of wrapping. */
  min-width: 0;
  overflow-wrap: anywhere;
}

.issue {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 14px 16px;
  margin-bottom: 10px;
  font-size: var(--text-sm);
}

.issue-head { display: flex; gap: 8px; margin-bottom: 6px; }
.issue blockquote {
  margin: 8px 0;
  padding-left: 12px;
  border-left: 3px solid var(--line);
  color: var(--muted);
  font-style: italic;
}
.issue p { margin: 6px 0; }

.tags-line { color: var(--muted); font-size: var(--text-xs); margin-top: var(--s-5); }
.tags-line a { color: var(--muted); }
.tags-line a:hover { color: var(--accent); }

.disclaimer {
  margin-top: 30px;
  padding: 14px 16px;
  border-radius: 10px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: var(--text-xs);
}

.event-log {
  max-height: 62vh;
  overflow-y: auto;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 8px 10px;
  font-size: var(--text-xs);
  scroll-behavior: smooth;
}

.event {
  display: grid;
  grid-template-columns: 66px 108px 158px 1fr;
  gap: 8px;
  align-items: baseline;
  padding: 6px 4px;
  border-bottom: 1px solid var(--line);
}

/* The log lives inside a panel, and a panel is already a card. A second
   box around the rows would say "widget inside widget"; the panel's own
   padding and the rows' hairlines do the separating. */
.panel .event-log {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
}

.event:last-child { border-bottom: none; }

.event time {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xs);
}

.event-type {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--accent);
  word-break: break-all;
}

.event-detail {
  color: var(--muted);
  overflow-wrap: anywhere;
  font-size: var(--text-xs);
}

.event .badge { justify-self: start; }

/* The controls of a stream are the controls of a timeline: a row of
   tabs, the one you are in underlined, and the state of the connection
   printed quietly beside them.

   The filter used to be the browser's own select and the autoscroll its
   own checkbox: a grey box and a blue square, the two loudest things on
   a page written in Ukrainian, and on a phone the row they sat in ran
   past the edge. A tab is a word and an underline; a switch is a chip
   that is filled or empty. The browser's own controls are gone. */
.filters--tabs .live-controls {
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
}

/* A tab that is also a button has to look exactly like a tab that is
   a link: the same word, the same underline, the same quiet weight. */
.filters--tabs button.filter {
  font-family: inherit;
  background: none;
  cursor: pointer;
}

.live-state {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-4);
}

/* The switch that used to be a checkbox is a chip: the same pill the
   feed uses for a mark, filled while the stream scrolls itself. */
.live-toggle {
  display: inline-flex;
  align-items: center;
  padding: 4px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font-family: inherit;
  font-size: var(--text-2xs);
  line-height: 1.2;
  cursor: pointer;
}
.live-toggle[aria-pressed="true"] {
  border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
  background: var(--accent-soft);
  color: var(--accent);
}

.live-count {
  color: var(--muted);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

.live-conn.is-live { color: var(--ok); }

/* The badge says one word at a time — "з'єднання…", then "наживо" — and
   a badge that changes its width would push the count beside it along
   the row the moment the stream answers. It holds its place instead. */
.live-conn {
  display: inline-block;
  min-width: 11ch;
}

@media (max-width: 760px) {
  .event { grid-template-columns: 58px 1fr; }
  .event-type { grid-column: 2; }
  .event-detail { grid-column: 1 / -1; padding-left: 2px; }
}

.reject-banner {
  margin: 18px 0 0;
  padding: 14px 16px;
  border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--danger) 40%, var(--line));
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
  color: var(--danger);
  font-size: var(--text-sm);
}

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--s-5);
  box-shadow: var(--shadow);
  margin-bottom: var(--s-4);
  overflow-x: auto;
}

/* A panel is as wide as the desk, but a sentence inside it is not: the
   line has to be one the eye can cross. The tables inside a panel keep
   the whole width — a ledger is a ledger. */
.panel > p {
  max-width: 76ch;
}

.panel-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 6px;
}

.panel-head h2 { margin: 0; }

.panel-actions {
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: var(--text-xs);
}

table { width: 100%; border-collapse: collapse; font-size: var(--text-xs); }
th, td { text-align: left; padding: 8px 6px; border-bottom: 1px solid var(--line); vertical-align: top; }
th { color: var(--muted); font-weight: 600; font-size: var(--text-2xs); text-transform: uppercase; letter-spacing: 0.04em; }
tr:last-child td { border-bottom: none; }
code { font-family: var(--font-mono); font-size: var(--text-xs); }
code.small { font-size: var(--text-2xs); color: var(--muted); word-break: break-all; }

button {
  appearance: none;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 600;
  padding: 9px 16px;
  cursor: pointer;
}
button:hover { filter: brightness(1.06); }

.muted { color: var(--muted); font-size: var(--text-xs); }

/* An empty state is a sentence under a rule. A dashed card announces
   itself as an empty box, which is not the news — the absence of the
   news is. */
.empty {
  background: none;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  padding: var(--s-5) 0;
  text-align: left;
}
.empty pre {
  display: inline-block;
  text-align: left;
  background: var(--surface-2);
  padding: 12px 16px;
  border-radius: 10px;
}

.notice-footer {
  margin: 0 0 var(--s-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  background: var(--surface);
}

.footer {
  padding: var(--s-3) 0 var(--s-6);
  border-top: 1px solid var(--line);
}

.footer-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  justify-content: space-between;
  color: var(--muted);
  font-size: var(--text-xs);
}

@media (max-width: 760px) {
  .hero { grid-template-columns: 1fr; align-items: start; }
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .nav-scroll {
    display: none;
  }
  .nav-scroll nav { gap: 14px; font-size: var(--text-xs); }
  .topbar-main { flex-wrap: wrap; padding: 10px 0; }
  .topbar-actions { order: 3; width: 100%; }
  .topbar-actions .topsearch { flex: 1; }
  .topsearch input { width: 100%; }
  .article { padding: 22px 18px; }
  .card { padding: 18px; }
  .lead-card { padding: 22px 18px; }
  .process-strip a { margin-left: 0; }
  .home-layout { grid-template-columns: 1fr; }
  .home-side { position: static; max-height: none; overflow: visible; }
  .feed-grid { grid-template-columns: 1fr; }
  .related-grid { grid-template-columns: 1fr; }
  /* The newsroom's own post is the first thing on a phone, so it must
     not push the news off the first screen. It keeps its words and
     loses its leading; its counters are already a quiet line. */
  .post--pinned .post-title { font-size: var(--text-lg); }
  .post--pinned .post-dek { font-size: var(--text-xs); }
  .ticker-row { min-height: 38px; padding: 4px 0; gap: var(--s-2); }
  .ticker-label { padding-right: var(--s-2); }
  .ticker-next { display: none; }
  .ticker-headline { max-width: 26ch; }
  .new-flash {
    left: var(--s-3);
    right: var(--s-3);
    bottom: 84px;
    justify-content: center;
  }
  .mobile-tabbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: var(--z-nav);
    display: flex;
    justify-content: space-around;
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
    background: var(--surface);
    border-top: 1px solid var(--line);
  }
  .mobile-tabbar a {
    display: grid;
    place-items: center;
    min-height: 44px;
    padding: 6px 10px;
    border-radius: var(--radius-md);
    color: var(--muted);
  }
  .icon-button { width: 44px; height: 44px; }
  .filter { min-height: 36px; display: inline-flex; align-items: center; }
  .stat-wide { grid-column: 1 / -1; }
  .mobile-tabbar a.active {
    /* The place you are in is marked by its colour, not by a chip
       under it. A filled pill on the bar says "this is a button that
       happens to be selected"; a blue word says "this is where you
       are", which is the same thing a timeline says. */
    color: var(--accent);
    background: none;
  }
  /* Six items over the safe area at 390px pushed the last ones past the
     viewport edge — the bottom-right "Статус" used to end up half-clipped.
     Tighter padding lets them all fit; the "Ще" menu still holds every
     section. */
  .mobile-tabbar a,
  .mobile-tabbar summary {
    padding: 6px 6px;
  }
  body { padding-bottom: 76px; }
}

pre {
  overflow-x: auto;
  padding: var(--s-3);
  border-radius: var(--radius-md);
  background: var(--surface-2);
}

/* The reason a decision was taken is the reason the operator came for.
   It used to be clamped to two lines, so the reason was cut off in the
   middle of the sentence — the data was on the page and the reader
   could not see it. */
.decision-reason {
  margin-top: var(--s-1);
  color: var(--muted);
  font-size: var(--text-2xs);
  max-width: 70ch;
}

@media (max-width: 760px) {
  .filter { min-height: 40px; }
  .filter-group {
    flex-basis: 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    mask-image: linear-gradient(to right, #000 94%, transparent);
    -webkit-mask-image: linear-gradient(to right, #000 94%, transparent);
  }
  .filter-group::-webkit-scrollbar { display: none; }
  .filter-group .filter { flex: 0 0 auto; }
  .filter-label {
    position: sticky;
    left: 0;
    z-index: 1;
    flex: 0 0 auto;
    align-self: stretch;
    display: inline-flex;
    align-items: center;
    background: var(--bg);
    padding-right: var(--s-1);
  }
  .panel-head:has(.filters),
  .panel-head:has(.panel-actions) {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s-2);
  }
  .copy-button,
  .share-button,
  .button {
    min-height: 40px;
  }
  .topsearch input { min-height: 44px; }
  .nav-scroll nav a {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
  }
}

.tabbar-more { position: relative; }
.tabbar-more summary {
  list-style: none;
  cursor: pointer;
}
.tabbar-more summary::-webkit-details-marker { display: none; }
.tabbar-more-menu {
  position: absolute;
  right: 0;
  bottom: calc(100% + 8px);
  z-index: var(--z-overlay);
  min-width: 190px;
  padding: var(--s-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow);
  display: grid;
  gap: 2px;
}
.tabbar-more-menu a {
  display: flex;
  align-items: center;
  min-height: 40px;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--radius-md);
  color: var(--text);
  font-size: var(--text-xs);
}
.tabbar-more-menu a:hover {
  background: var(--surface-2);
  text-decoration: none;
}
@media (max-width: 760px) {
  .mobile-tabbar summary {
    display: grid;
    place-items: center;
    min-height: 44px;
    padding: 6px 10px;
    border-radius: var(--radius-md);
    color: var(--muted);
    list-style: none;
  }
  /* The section the reader is in sits inside the menu, so the menu is
     the marked place: the phone lights the tab that holds it. */
  .tabbar-more.active summary {
    color: var(--accent);
    background: none;
  }
  .tabbar-more-menu a.active {
    color: var(--accent);
    font-weight: var(--font-weight-semibold);
  }
}

.filter-count {
  margin-left: 4px;
  opacity: 0.62;
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xs);
}

/* The feed's filters are a tab row, the way the top of a timeline is:
   the tab you are in is underlined, the rest are quiet words. A pill
   says "button"; an underline says "you are in this section". The row
   loses its own rule — the active tab's underline is the rule. */
.filters--tabs {
  border-bottom: 0;
  gap: 0 var(--s-5);
}

.filters--tabs .filter-group { gap: var(--s-4); }

.filters--tabs .filter {
  padding: 9px 0 10px;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: none;
  color: var(--muted);
  font-size: var(--text-xs);
}

.filters--tabs .filter:hover {
  color: var(--text);
  border-bottom-color: var(--line);
}

.filters--tabs .filter.active {
  background: none;
  color: var(--text);
  font-weight: var(--font-weight-semibold);
  border-bottom-color: var(--accent);
}

.filters--tabs .filter.active .filter-count { opacity: 0.7; }

/* The right column of the feed is a column of cards, the way x.com's
   is: a bounded, rounded block per topic, a bold name across the top, a
   rule under it.

   The rule it used to follow — "no card in the context column, a box
   inside a box is what the timeline never does" — was right about the
   timeline and wrong about the rail. The feed itself stays cardless:
   a box in the middle of the run of posts breaks the run, and the run
   is the feed. But the rail is context, and context is allowed to be a
   box: that is how a reader finds the topics, the live threads and the
   note about the company without reading the posts at all. */
.home-side .panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: none;
  padding: 0;
  margin-bottom: 0;
  overflow: hidden;
}

/* What is happening now: a ranked list, not a chart. */
.trend-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.trend-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  padding: 7px 0;
  color: var(--muted);
  font-size: var(--text-sm);
  border-bottom: 1px solid var(--line);
}
.trend-list li:last-child .trend-item { border-bottom: 0; }
.trend-item:hover { color: var(--text); text-decoration: none; }
.trend-name { font-weight: var(--font-weight-medium); }
.trend-count {
  color: var(--muted-strong);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}

.beat-bars {
  display: grid;
  gap: var(--s-2);
}

.beat-bar {
  display: grid;
  grid-template-columns: 78px minmax(0, 1fr) 24px;
  gap: var(--s-2);
  align-items: center;
  color: var(--muted);
  font-size: var(--text-2xs);
}

.beat-bar:hover {
  color: var(--text);
  text-decoration: none;
}

.beat-bar-track {
  height: 7px;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
}

.beat-bar-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
}

.beat-bar:hover .beat-bar-fill { background: var(--accent-strong); }

.beat-bar-count {
  text-align: right;
  color: var(--muted);
  font-weight: var(--font-weight-semibold);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 760px) {
  .beat-bar { grid-template-columns: 68px minmax(0, 1fr) 22px; }
}

@media (max-width: 760px) {
  .decision-table,
  .decision-table tbody,
  .decision-table tr,
  .decision-table td {
    display: block;
    width: 100%;
  }
  .decision-table thead { display: none; }
  .decision-table tr {
    margin-bottom: var(--s-3);
    padding: var(--s-3);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface);
  }
  .decision-table td {
    display: block;
    padding: var(--s-1) 0;
    border: 0;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .decision-table td::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    color: var(--muted);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
  }
  .decision-table td .badge { margin-right: 4px; }
  .decision-table td[data-label="Матеріал"]::before {
    margin-bottom: 2px;
  }
}

.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;
}

@media (max-width: 760px) {
  .stack-table,
  .stack-table tbody,
  .stack-table tr,
  .stack-table td {
    display: block;
    width: 100%;
  }
  .stack-table thead { display: none; }
  .stack-table tr {
    margin-bottom: var(--s-3);
    padding: var(--s-3);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface);
  }
  .stack-table td {
    display: block;
    padding: var(--s-1) 0;
    border: 0;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  .stack-table td::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    color: var(--muted);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
  }
  .stack-table td .badge { margin-top: 2px; }
  .stack-table td.is-empty { display: none; }
}

@media (max-width: 760px) {
  .ops-stack,
  .ops-stack tbody,
  .ops-stack tr,
  .ops-stack td {
    display: block;
    width: 100%;
  }
  .ops-stack thead { display: none; }
  .ops-stack tr {
    margin-bottom: var(--s-3);
    padding: var(--s-3);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface);
  }
  .ops-stack td {
    display: grid;
    grid-template-columns: 110px minmax(0, 1fr);
    gap: var(--s-2);
    padding: var(--s-1) 0;
    border: 0;
    white-space: normal;
  }
  .ops-stack td::before {
    content: attr(data-label);
    color: var(--muted);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
  }
  .ops-stack td .badge { justify-self: start; }
  .ops-stack td:first-child {
    display: block;
    margin-bottom: var(--s-2);
    font-size: var(--text-base);
  }
  .ops-stack td:first-child::before { display: none; }
}

/* The options a blocked agent offered. The arbiter's answer is only
   meaningful next to the choices it chose between, so the journal keeps
   them visible rather than collapsed into a note. */
.arbiter-options {
  margin-top: var(--s-2);
}

.arbiter-options summary {
  cursor: pointer;
  color: var(--muted);
  font-size: var(--text-2xs);
  list-style: revert;
}

.arbiter-options summary:hover {
  color: var(--accent);
}

.arbiter-options ul {
  margin: var(--s-1) 0 0;
  padding-left: 18px;
  color: var(--muted);
  font-size: var(--text-2xs);
  line-height: 1.5;
}

.arbiter-options li {
  margin-bottom: 3px;
}

/* ───────────────────────────────────────────────────────────────────
   The x.com shell.

   On a wide screen the page is three columns: a rail of sections on the
   left, the feed in the middle, the side panels on the right. The
   wordmark moves into the rail, the header keeps only the search and
   the theme, and the sections under the header are gone — the rail
   holds them. Below the breakpoint the rail is dropped and the same
   sections, from the same list, appear as the scrollable row under the
   header: the app loses the shape, never a section. */
.rail { display: none; }
.topbar-title { display: none; }

@media (min-width: 1100px) {
  body { padding-left: 268px; }

  .rail {
    display: flex;
    flex-direction: column;
    position: fixed;
    inset: 0 auto 0 0;
    width: 268px;
    padding: 10px 12px 24px;
    border-right: 1px solid var(--line);
    background: var(--bg);
    overflow-y: auto;
    z-index: var(--z-nav);
  }

  .rail-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px 16px;
    color: var(--text);
    font-size: var(--text-lg);
    font-weight: var(--font-weight-bold);
    letter-spacing: var(--tracking-tight);
  }
  .rail-brand:hover { text-decoration: none; }

  .rail-nav { display: grid; gap: 2px; }
  .rail-nav a {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 9px 12px;
    border-radius: 999px;
    color: var(--muted);
    font-size: var(--text-sm);
    transition: color var(--duration-fast) var(--ease),
      background-color var(--duration-fast) var(--ease);
  }
  .rail-nav a:hover {
    color: var(--text);
    background: var(--surface-2);
    text-decoration: none;
  }
  .rail-nav a.active {
    color: var(--text);
    font-weight: var(--font-weight-bold);
  }
  .rail-nav svg { flex: none; }

  .rail-foot { margin-top: auto; padding-top: 20px; }
  .rail-chip {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 10px 13px;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--muted);
    font-size: var(--text-xs);
  }
  .rail-chip:hover {
    color: var(--text);
    border-color: var(--rule);
    text-decoration: none;
  }

  /* The wordmark is in the rail now, and the header says which section
     the reader is in — the way a timeline's header names the timeline.
     The header sits on the page's own black, not on a lighter band: the
     rail and the header are one continuous field. */
  .topbar-main .brand { display: none; }
  .topbar { background: var(--bg); }

  /* The detail page is a reading column, the way one post's page is one
     column on a timeline: the headline and the dek get a measure they
     can be read at, and the rest of the width stays empty instead of
     stretching the type. And it is flat — a post's own page is not a
     card, so nothing here is boxed either; only the audit blocks below
     the text keep their frames, because they are evidence. */
  .article {
    background: none;
    border: 0;
    border-radius: 0;
    padding: 0;
    box-shadow: none;
  }
  .topbar-title {
    display: block;
    font-size: var(--text-lg);
    font-weight: var(--font-weight-bold);
    letter-spacing: var(--tracking-tight);
  }
  .nav-scroll { display: none; }

  /* The hero's numbers are a row under the headline, the way a profile
     prints its counts: one flat line of facts, not a narrow column of
     tiles squeezed beside the text. */
  .hero {
    grid-template-columns: 1fr;
    align-items: start;
  }
  /* The hero's numbers are a row under the headline, the way a profile
     prints its counts: one flat line of facts, not a narrow column of
     tiles squeezed beside the text. They are sized by their own labels
     and stand shoulder to shoulder, so a page with two numbers does not
     spread them across the whole width. */
  .hero {
    grid-template-columns: 1fr;
    align-items: start;
  }
  .stat-grid {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-4) var(--s-5);
    max-width: 760px;
  }
  .stat-grid .stat { min-width: 0; }
}

/* ───────────────────────────────────────────────────────────────────
   The post.

   The feed's unit is a post: an avatar, a name row, a headline, a
   summary, and a row of quiet counts under it. Hairlines divide the
   posts; there is no box around them. A box says "this is a card" — a
   timeline says "this is the next thing that happened".

   Nothing in the post is decoration: the numbers in the action bar are
   the same facts the card used to print (how many sources, that the
   Verifier checked it, how many issues it left, how confident the
   editor was), and the disclosure label stays on the post, because the
   reader has to be able to see that the text was machine-written. */
.post {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: 12px;
  padding: 14px 12px 16px;
  border-bottom: 1px solid var(--line);
  min-width: 0;
  transition: background-color var(--duration-fast) var(--ease);
}

.post:hover {
  background: color-mix(in srgb, var(--ink) 3%, transparent);
}

.post-avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--beat, var(--accent)) 20%, var(--surface-2));
  color: var(--ink);
  font-size: var(--text-lg);
  font-weight: var(--font-weight-bold);
  line-height: 1;
}

.post-main { min-width: 0; }

.post-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: 1.5;
}

.post-name {
  color: var(--text);
  font-weight: var(--font-weight-bold);
}

/* The verifier's mark, next to the name — a timeline's badge. */
.post-verified {
  display: inline-flex;
  color: var(--accent);
  vertical-align: -2px;
}
.post-verified svg { width: 14px; height: 14px; }

/* A mark next to a name is a word, not a stamped label. The byline of
   a timeline reads "Name ✓ @handle · 2h": nothing in it is shouted in
   capitals, and the word that carries the meaning is the handle. The
   colours still say what a mark means — a live story is red, a
   correction is amber — the case just stops doing that job twice. */
.post-tag {
  color: var(--muted);
  font-size: var(--text-xs);
}

.post-tag--update { color: var(--ok); }
.post-tag--correction { color: var(--warn); }
/* A live story is not a tag like the rest: it carries the pulsing dot
   that tells the reader the stream is still running. */
.post-tag--live {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--danger);
}
.post-dot { color: color-mix(in srgb, var(--muted) 60%, transparent); }
.post-time { color: var(--muted); }

/* A dot between two facts is a separator, and a separator that can be
   left alone is not a separator any more — it is a stray mark at the
   end of a line, or at the start of the next one.

   The name row is a flex row, and the dot was a flex item of its own:
   a narrow column could push it to the end of a line ("Суспільство ·"
   / "14 хв тому") or to the head of the next one. The dot is now drawn
   by the fact it follows, so it travels with that word and can never
   stand alone. The spacing is unchanged: the dot takes the room the
   flex gap used to give it on the left, and the gap itself gives the
   room on the right. */
@supports selector(:has(*)) {
  .post-head .post-dot,
  .post-meta .post-dot { display: none; }

  .post-head > *:has(+ .post-dot)::after,
  .post-meta > *:has(+ .post-dot)::after {
    content: "·";
    margin-left: 6px;
    color: color-mix(in srgb, var(--muted) 60%, transparent);
  }
}

/* The chain of facts in a name row — the marks of a material and the
   time it was written — is one fact with parts, not three facts on a
   shelf. So it is one box: a narrow column moves the whole chain to
   the next line instead of breaking it.

   The chain does not fold. A fold inside the chain would land between
   two facts, and every such landing is next to a separator: the dot
   would be left at the end of a line or pushed to the head of the
   next one, alone. A chain that is too long for the line shrinks
   instead — a fact may fold inside its own box, but the chain keeps
   its shape, and no separator is ever left on its own. */
.post-meta {
  display: inline-flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 6px;
  min-width: 0;
}

/* No chain opens with a separator. A dot separates two facts, so the
   templates only put one between two facts: a name row with no handle
   opens with the time, and a post with no mark and no beat opens with
   the time too. */

.post-flag {
  color: var(--accent);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-bold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
}

.post-title {
  margin: 3px 0 4px;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: var(--font-weight-bold);
  line-height: 1.26;
  letter-spacing: var(--tracking-tight);
}

.post-title a { color: var(--text); }
.post-title a:hover { color: var(--accent); text-decoration: none; }

.post-dek {
  margin: 0 0 10px;
  color: var(--muted);
  font-size: var(--text-sm);
  line-height: 1.55;
  max-width: 68ch;
}

.post-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  color: var(--muted);
  font-size: 12px;
}

/* The disclosure is a fact about the text, so it is printed like the
   other facts of the bar: quiet words, no frame. A boxed chip in a row
   of counts is the one thing in that row that reads as a control, and
   a control in the middle of a post is a control that goes nowhere. */
.post .ai-tag {
  padding: 0;
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-medium);
  background: none;
  border: 0;
  color: var(--muted);
}

.post-action {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.post-action svg { flex: none; }
.post-action:hover { color: var(--text); }
.post-action--warn { color: var(--warn); }
/* A level wears its colour — the same three the risk wears, so a word
   and a colour mean one thing across the site. A count of notes wears
   the colour of the worst note behind it, not the colour of "there is
   a count": three small notes are three small things, and one serious
   note is a problem. */
.post-action--level-low { color: var(--ok); }
.post-action--level-medium { color: var(--warn); }
.post-action--level-high { color: var(--danger); }
.post-action--note { color: var(--muted); }
.post-action--num { color: var(--muted-strong); font-weight: var(--font-weight-semibold); }
/* The disclosure and the link are one tail: they wrap together, or not
   at all, so a lone pill never ends a row on its own. */
.post-tail {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-left: auto;
}

.post-action--read {
  /* A link is blue. The site's other link-coloured words are blue at
     rest, and the one link in a post's action bar was the only one
     that stayed black until the reader pointed at it. */
  color: var(--accent);
  font-weight: var(--font-weight-semibold);
}
.post-action--read:hover { color: var(--accent-strong); }

/* The tiers, from the format the Scout assigned: the lead is the big
   one at the top, a briefing is a line. The difference is in the type
   and the weight, not in a box around it. */
.post--lead .post-title {
  font-size: clamp(24px, 2.6vw, 32px);
  line-height: 1.18;
}
.post--lead .post-dek { font-size: var(--text-base); }
.post--key .post-title { font-size: clamp(20px, 2vw, 24px); }
.post--brief .post-title { font-size: var(--text-base); font-weight: var(--font-weight-semibold); }
.post--brief .post-dek { font-size: var(--text-xs); }
.post--breaking .post-avatar {
  background: color-mix(in srgb, var(--breaking-bg) 26%, var(--surface-2));
}

/* A feed of posts is one column divided by hairlines, not a stack of
   boxes with gaps between them. */
.feed--posts {
  gap: 0;
  border-top: 1px solid var(--line);
}

/* One rule between two posts, not two. The lead already draws the
   line under itself, so the feed must not draw another one above its
   first post. */
.home-main > .post + .feed--posts { border-top: 0; }

.post--fresh { animation: fresh-in var(--duration-slow) var(--ease) both; }

@media (max-width: 620px) {
  .post { grid-template-columns: 36px minmax(0, 1fr); gap: 10px; padding: 12px 4px 14px; }
  .post-avatar { width: 36px; height: 36px; font-size: var(--text-base); }
  .post-actions { gap: 12px; }
  .post-action--read { margin-left: 0; }
  /* A headline is set for the column it is read in. On a phone that
     column is 300 pixels wide, and a 24-pixel headline breaks into
     seven lines before it says anything. The phone gets the type of a
     timeline: the same hierarchy, one step smaller. */
  .post-title { font-size: var(--text-lg); }
  .post--lead .post-title { font-size: 21px; line-height: 1.24; }
  .post--key .post-title { font-size: var(--text-lg); }
  .post--brief .post-title { font-size: var(--text-sm); }
  .post--lead .post-dek { font-size: var(--text-sm); }
}

/* A list in the centre column is a column of rows, not a stack of
   boxes. The rows are divided by a hairline and keep the beat colour as
   a rule on the left, so the reader still sees which desk a row belongs
   to — without the row being framed. */
.feed--flat {
  gap: 0;
  border-top: 1px solid var(--line);
}

.feed--flat > .card {
  background: none;
  border: 0;
  border-bottom: 1px solid var(--line);
  /* The rule takes the beat's colour when the row has one, and is
     invisible when it does not — a grey bar down the side of a list
     that carries no desk is decoration, and decoration is what this
     column stopped having. */
  border-left: 3px solid var(--beat, transparent);
  border-radius: 0;
  /* No left padding: a row's text starts where the page's title
     starts, the way a post's text starts under the column's name. */
  padding: var(--s-4) 0;
  box-shadow: none;
  transition: background-color var(--duration-fast) var(--ease);
}

/* A row in the log tints under the cursor, the way a post in the feed
   does — the same hand is passing over both. */
.feed--flat > .card:hover {
  background: color-mix(in srgb, var(--ink) 3%, transparent);
}

.feed--flat > .card:last-child { border-bottom: 0; }

/* A row in a log is a headline among other headlines, so it takes the
   headline's size, not a poster's: in a narrow column the page's
   biggest type would belong to the first row and to nothing else. */
.feed--flat > .card h2 {
  font-size: clamp(19px, 1.9vw, 23px);
  line-height: 1.25;
}

/* The log pages are read, not scanned: the corrections, the rejected
   material, the cycles. Their prose needs a line the eye can cross, so
   the page takes the article's measure. Without this the sentence in a
   correction ran the whole width of the desk.

   The column is anchored to the desk's left edge, not centred inside
   it. Centred put the text 130px further right than the feed, and the
   reader's eye had to travel on every navigation. */
.read-col {
  max-width: var(--col-main);
  margin-inline: 0;
}

/* The cycles page is a ledger, not an essay: it needs a little more
   room for the row of counters under each headline. */
.read-col--wide { max-width: 980px; }

/* Inside that measure the hero is one column — a two-column hero in a
   780px column is two narrow columns side by side, which is not a hero
   at all — and its numbers line up in a row like the ones on the front
   page. */
.read-col .hero {
  grid-template-columns: 1fr;
  align-items: start;
  gap: var(--s-4);
}

.read-col .stat-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4) var(--s-5);
}

/* A panel in the reading column is a section of the page, not a widget
   floating in it: it keeps its heading and its rule and drops the box. */
.read-col .panel {
  background: none;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  padding: var(--s-5) 0;
}

/* The numbers in a panel stand shoulder to shoulder, the way a profile
   prints its counts. A grid that stretches them to the panel's width
   leaves one counter stranded in the middle of the room. */
.pulse-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 10px var(--s-4);
  margin: var(--s-4) 0 8px;
}

.pulse-grid .kpi { flex: 0 0 auto; }

/* The items of a cycle are the cycle's own lines, not boxes inside the
   cycle's box: a left rule and a line of text, the same way the feed
   itself is drawn. */
.cycle-items li {
  background: none;
  border: 0;
  border-left: 2px solid var(--line);
  border-radius: 0;
  padding: 6px 0 6px 10px;
}

/* The byline above a headline: the same name row a post carries in the
   feed — avatar, name, the verifier's mark, the handle, the time — so
   an article opened on its own looks like the post it came from. The
   disclosure travels with it on the right: the reader must be able to
   see that the text was machine-written before they start reading. */
.byline {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line);
}

.byline-avatar {
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--beat, var(--accent)) 20%, var(--surface-2));
  color: var(--ink);
  font-size: var(--text-base);
  font-weight: var(--font-weight-bold);
  line-height: 1;
}

.byline-id {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-width: 0;
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: 1.5;
}

.byline-name {
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: var(--font-weight-bold);
}

.byline-handle { color: var(--muted); }
.byline-time { color: var(--muted); }

/* The dot and the timestamp are one thing: a line break between them
   leaves a dot hanging at the end of the line above. */
.byline-when {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}

.byline-tail {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* The facts under the headline are a caption, not a shelf of pills
   above it — the headline meets the eye first, the way it does on a
   timeline. The row still ends in the rule that closes the head of the
   page, so the text below starts on a clean line. */
.article-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin: 0 0 var(--s-5);
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-xs);
}

/* The dek no longer carries the rule — the caption row under it does —
   so the two lines read as one head instead of two boxes. */
.article .article-dek {
  margin: 0 0 14px;
  padding-bottom: 0;
  border-bottom: 0;
}

@media (max-width: 760px) {
  /* On a phone the name row and the disclosure pill cannot share one
     line: the pill needs a line of its own, under the name. */
  .byline { flex-wrap: wrap; }
  .byline-id { flex: 1 1 auto; }
  .byline-tail { margin-left: 0; order: 3; }
}

/* ───────────────────────────────────────────────────────────────────
   The desk.

   Every page on the site is the same two columns: the text on the
   left, the context on the right. The widths used to be written out
   per page — 696 on the feed, 716 on a live story, 780 on a material,
   1040 on a report — so the text sat under a different x on every page
   and re-wrapped the moment the reader moved. That is the jump: not
   a broken rule, but a column that moves under the eye.

   Now the column is a property of the desk. The feed, a material, a
   live story and the reports all open in the same place, and the right
   column is the same width on all of them. */

.desk {
  display: grid;
  grid-template-columns: minmax(0, var(--col-main)) var(--col-side);
  gap: var(--col-gap);
  align-items: start;
}

.desk-main { min-width: 0; }

.desk-side {
  min-width: 0;
  display: grid;
  gap: var(--s-4);
  align-content: start;
  position: sticky;
  top: calc(var(--topbar-h, 96px) + var(--s-3));
}

/* Below the width where the two columns still read as two columns,
   the desk becomes one and the context follows the text. */
@media (max-width: 900px) {
  .desk {
    grid-template-columns: minmax(0, 1fr);
  }
  .desk-side {
    position: static;
    margin-top: var(--s-5);
  }
}

/* The feed and the live story are the desk too — same columns, same
   gutter, so the reader crosses from one to the other without the
   text moving. */
.home-layout,
.liveblog-layout {
  grid-template-columns: minmax(0, var(--col-main)) var(--col-side);
  gap: var(--col-gap);
}

@media (max-width: 900px) {
  .home-layout,
  .liveblog-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* A material is a post opened on its own: the same column the feed
   reads in, with the same context column beside it. */
.article {
  display: grid;
  grid-template-columns: minmax(0, var(--col-main)) var(--col-side);
  gap: var(--col-gap);
  align-items: start;
}

.article-main { min-width: 0; }

.article-side {
  min-width: 0;
  display: grid;
  gap: var(--s-4);
  align-content: start;
  position: sticky;
  top: calc(var(--topbar-h, 96px) + var(--s-3));
}

/* The side column of a material is a column of cards, the same as the
   rail of every other page.

   It used to be a quiet column of hairlines, on the principle that a
   filled box beside the text is the box-in-a-box the timeline never
   has. That principle was right about the *feed*, where the run of
   posts must not be broken by a box; the context beside a material is
   not the feed, and a reader who has opened a material is looking for
   its side column as a set of things — how it was made, what is
   confirmed, what is not — not as a column of rules. */
.article-side .ai-disclosure,
.article-side .evidence-part {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 0;
  overflow: hidden;
}
.article-side .ai-disclosure { margin: 0; }
.article-side .ai-disclosure summary {
  margin: 0;
  padding: 12px var(--s-4);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-lg);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--text);
}
.article-side .ai-disclosure[open] summary { margin-bottom: 0; }
.article-side .ai-disclosure-body {
  padding: var(--s-4);
  color: var(--muted-strong);
  font-size: var(--text-sm);
  line-height: 1.5;
}
.article-side .disclosure-facts li {
  background: none;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  padding: var(--s-3) 0;
}
.article-side .disclosure-facts { gap: 0 var(--s-4); }
/* The name in the head of a card in the side column, and the line
   under it that says which pipeline did the work. */
.side-card-title {
  font-size: var(--text-lg);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--text);
}
.article-side .disclosure-pipeline {
  margin: 0 0 10px;
  color: var(--muted);
  font-size: var(--text-2xs);
}
.article-side .disclosure-accountability {
  background: none;
  border-left: 2px solid var(--accent);
  padding: 4px 0 4px 12px;
}

/* The facts under the headline are a caption, so they read as a line of
   quiet words, not a shelf of filled pills. The words are separate
   items, so they are separated with room and not with a middot: a
   mark between them would float in the middle of the gap, and a
   wrapped line would strand it at its end. */
.article-meta {
  gap: 2px 14px;
}
.article-meta > * {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  color: var(--muted);
}

/* The byline's disclosure is a word beside the name, not a blue pill. */
.byline-tail .ai-tag {
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--muted-strong);
  font-weight: var(--font-weight-medium);
}

/* The marker in the disclosure's heading is a label, not a button. */
.ai-disclosure summary .ai-tag {
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--muted-strong);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-wide);
}

/* The live story's side column is a column of cards, the same as the
   rail of every other page: a bounded, rounded block, a name across
   the top, a rule under it. */
.liveblog-side .panel,
.liveblog-side .liveblog-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: none;
  padding: 0;
  margin-bottom: 0;
  overflow: hidden;
}
.liveblog-side .liveblog-panel h2 {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: var(--font-weight-bold);
  text-transform: none;
  letter-spacing: var(--tracking-tight);
  color: var(--text);
}
.liveblog-side .liveblog-panel > h2,
.liveblog-side .liveblog-panel--unknown > summary {
  padding: 12px var(--s-4);
  border-bottom: 1px solid var(--line);
}
.liveblog-side .liveblog-panel .liveblog-facts {
  margin: 0;
  padding: var(--s-4) var(--s-4) var(--s-4) calc(var(--s-4) + 18px);
}
.liveblog-side .liveblog-panel > p {
  margin: 0;
  padding: 10px var(--s-4);
  font-size: var(--text-sm);
}
.liveblog-side .liveblog-panel summary { cursor: pointer; }
.liveblog-side .liveblog-panel summary h2 { display: inline; }
.liveblog-side .liveblog-panel--unknown { border-style: solid; }

/* The facts of one update read as a line of words, not a shelf of
   pills — separated with room, the way the feed separates its counts,
   and not with a middot that a wrapped line would strand. */
.liveblog-timeline .card-meta {
  gap: 2px 14px;
}
.liveblog-timeline .card-meta > * {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  color: var(--muted);
}

@media (max-width: 900px) {
  .article {
    grid-template-columns: minmax(0, 1fr);
  }
  .article-side {
    position: static;
    margin-top: var(--s-5);
  }
}

/* The numbers under a headline are a row of figures, the way a profile
   prints its counts — not a grid of tiles. */
.stat-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4) var(--s-5);
}

.stat-grid .stat { min-width: 0; }

/* ───────────────────────────────────────────────────────────────────
   A card inside a card is a card inside a card.

   The desk has one kind of box — the panel — and the panels that hold
   a list of things hold it as a list: rows separated by a rule, with a
   line down the left where the item is marked. A framed thing inside
   a framed thing says "this is a thing of its own", and the page
   starts to look like a drawer of drawers instead of a page. */

.panel .agent-card,
.panel .standard-list li,
.panel .autonomy-item,
.panel .cycle-items li,
.panel .cycle-error,
.panel .cycle-errors li {
  background: none;
  border: 0;
  border-left: 2px solid var(--line);
  border-radius: 0;
  padding: 8px 0 8px 12px;
}

.panel .cycle-error,
.panel .cycle-errors li {
  border-left-color: var(--danger);
  color: var(--danger);
}

/* The pipeline is a line of stages, not a diagram in a frame. */
.panel .pipeline {
  background: none;
  border: 0;
  border-left: 2px solid var(--line);
  border-radius: 0;
  padding: 8px 0 8px 12px;
}

/* A row of figures reads as figures — the way a profile prints its
   counts — not as a shelf of chips. */
.cycle-stats .badge,
.panel .kpi .badge {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  color: var(--muted);
}

/* The name row outside the feed: the same identity a post carries,
   smaller, for a row in a log. */
.name-row {
  gap: 6px;
  margin-bottom: 6px;
}

.post-avatar-sm {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: inline-grid;
  place-items: center;
  background: color-mix(in srgb, var(--beat, var(--accent)) 20%, var(--surface-2));
  color: var(--ink);
  font-size: var(--text-xs);
  font-weight: var(--font-weight-bold);
  line-height: 1;
  flex: none;
}

/* The handle of the account that published: a word next to the name,
   in the quiet colour, the way a timeline prints it. */
.post-handle {
  color: var(--muted);
  font-size: var(--text-xs);
}

/* A material is a post opened on its own: the avatar stands in the
   gutter, the text under it, exactly as in the feed. The headline of
   a material therefore opens under the headlines of the feed, and the
   reader's eye does not have to travel when they open one. */
.article-main {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  /* The same inset the feed's posts carry, so the avatar of a material
     stands where the avatar of a post stands and the text of a material
     begins where the text of a post begins. */
  padding: 0 12px;
}

.article-col { min-width: 0; }

.article-avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--beat, var(--accent)) 20%, var(--surface-2));
  color: var(--ink);
  font-size: var(--text-lg);
  font-weight: var(--font-weight-bold);
  line-height: 1;
}

@media (max-width: 620px) {
  .article-main {
    grid-template-columns: 36px minmax(0, 1fr);
    gap: 10px;
  }
  .article-avatar {
    width: 36px;
    height: 36px;
    font-size: var(--text-base);
  }
}

/* The head of a live story carries the same gutter as a material and
   the same post as the feed: the avatar stands where the avatar of a
   post stands, and the headline begins where the headline of a post
   begins. */
.liveblog-head {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  gap: 12px;
  padding: 0 12px;
}

.liveblog-head-col { min-width: 0; }

@media (max-width: 620px) {
  .liveblog-head {
    grid-template-columns: 36px minmax(0, 1fr);
    gap: 10px;
  }
  .liveblog-head .article-avatar {
    width: 36px;
    height: 36px;
    font-size: var(--text-base);
  }
}

/* ───────────────────────────────────────────────────────────────────
   The phone: nothing may push the page sideways.

   At 390px the column was wider than the screen, so the ticker, the
   search field and a line of the post body ran off the right edge and
   the page scrolled horizontally under the reader's thumb. The causes
   are different elements; the rule is one: every box may shrink, and a
   long word may break before the layout does. */
@media (max-width: 640px) {
  html, body { max-width: 100%; overflow-x: hidden; }
  .search { flex: 1 1 auto; min-width: 0; }
  .search input { width: 100%; min-width: 0; }
  .post, .log-row, .hero, .desk-main, .home-main, .desk, .home-layout {
    min-width: 0;
  }
  .post p, .post-body, .lede, .dek, .log-body, .legal-body p {
    overflow-wrap: anywhere;
  }
}

/* ───────────────────────────────────────────────────────────────────
   The phone, second pass: what the audit measured.

   The overflow was fixed already; these are the things a thumb and an
   eye hit. Tap targets were 16–20px high (a link is not a target at
   that size), the text that carries meaning sat at 10–11px, the
   sticky header covered 25% of the screen, an anchor jump landed under
   it, the article column was 252px wide (27 characters a line), and the
   ticker scrolled by itself with no way to stop it — or to read it. */
@media (max-width: 640px) {
  /* An anchor stops below the header and the ticker, not under them. */
  [id] { scroll-margin-top: 240px; }

  /* A target is at least 44px high. */
  .post-action,
  .share-button,
  .filter,
  .topbar-back,
  .pager a,
  .page-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  .topbar-back { min-width: 44px; justify-content: center; }

  /* The small type that says what a thing is: 12px, not 10. */
  .post-action,
  .post-time,
  .ticker-age,
  .ticker-label,
  .badge,
  .ai-tag,
  .filter-count,
  .byline-when,
  .log-actions {
    font-size: var(--text-xs);
  }

  /* The article's own measure: give the text the screen back. */
  .article { padding-left: 0; padding-right: 0; }
  .article .article-main { padding-left: 0; padding-right: 0; }
  .article .article-col { padding-left: 0; padding-right: 0; }

  /* The ticker stops moving and becomes something you can swipe. */
  .ticker-track { animation: none !important; }
  .ticker-viewport { overflow-x: auto; -webkit-overflow-scrolling: touch; }

  /* The header loses the second row it did not need. */
  .search { max-width: none; }
}

/* Small, named jobs the templates used to do with a `style` attribute.
   An attribute cannot carry a CSP nonce, so every one of them kept
   `style-src 'unsafe-inline'` alive. A class can be trusted. */
.plain-label { font-weight: 400; text-transform: none; letter-spacing: 0; }
.mt-3 { margin-top: var(--s-3); }
.mb-2 { margin: 0 0 8px; }
.note-sm { font-size: var(--text-sm); color: var(--muted); }
.push-right-xs { margin-left: auto; font-size: var(--text-2xs); }
.mt-2-xs { margin: var(--s-2) 0 0; font-size: var(--text-2xs); }

/* The companion's composer: a box you write in, the way a timeline's
   post box is — a rule above, a field, one button on the right. */
.composer {
  border-bottom: 1px solid var(--line);
  padding: var(--s-4) var(--s-4) var(--s-3);
}
.composer-form { display: grid; gap: 10px; }
.composer textarea {
  width: 100%;
  min-height: 76px;
  resize: vertical;
  padding: 12px var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--text-base);
  line-height: 1.45;
}
.composer textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.composer-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}
.composer-hint { color: var(--muted); font-size: var(--text-xs); }
.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;
}

/* ───────────────────────────────────────────────────────────────────
   An agent has one face.

   The drawing is derived from the platform's deterministic genome: the
   archetype gives the hue, the silhouette gives the body, the signature
   pose gives the mark. The same agent is the same character in the rail
   of the pipeline, on /editor, on /arbiter and in the chat. */
.agent-avatar {
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: 50%;
  line-height: 0;
  vertical-align: middle;
  overflow: hidden;
  background: var(--surface-2);
}
.agent-avatar svg { display: block; }
.agent-avatar svg > circle:first-of-type { stroke: rgba(255, 255, 255, 0.09); stroke-width: 1; }

/* The pipeline as a row of faces: the four characters, in the order the
   work goes through them. */
.pipeline-faces {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  margin: 10px 0 0;
  font-size: var(--text-2xs);
  color: var(--muted);
}
.pipeline-faces .step {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.pipeline-faces .arrow { color: var(--line-strong); }
.pipeline-faces .who { color: var(--text); }
.pipeline-faces--small { gap: 4px 6px; margin: 0 0 10px; font-size: var(--text-2xs); }

/* ───────────────────────────────────────────────────────────────────
   The companion: one conversation, the whole free space.

   Not a page with a side column — a chat. The log scrolls, the composer
   sits under the thumb, and the empty state offers four ways in. The
   measure is the timeline's own (var(--chat-measure)), so the conversation reads at
   the same width as the feed beside it.

   The rule that keeps it from becoming a dashboard: borders separate
   zones, they do not decorate messages. Everything a turn does — a
   search, a material read, the answer itself — is laid out as one
   column of text, the way a post is. */
.chat {
  /* The conversation is measured like the feed beside it: the timeline's
     own 600px column. A wider line of larger prose would be a second,
     louder layout on the same screen. */
  --chat-measure: 600px;
  /* A column, not a grid: the panel is hidden on most visits, and a grid
     row that vanishes for a hidden child re-assigns every following
     track — the composer ended up in the 1fr row and off the screen. */
  display: flex;
  flex-direction: column;
  /* The composer ends at the viewport's edge, not 44px above it: the chat
     is a place, so nothing — no strip, no footer — sits under it. */
  height: calc(100dvh - var(--topbar-h, 96px));
}
.chat-head, .chat-panel, .chat-composer { flex: 0 0 auto; }
/* main sheds the wrap's measure and gutter on this route; the log centres
   itself, the head and the composer keep their borders full-bleed. */
.chat-main { max-width: none; margin: 0; padding: 0; }
/* The phone keeps a bottom pad for the tab bar; the chat already ends
   above that bar, so the pad would push it 19px past the screen. */
body.chat-page { padding-bottom: 0; }

/* ── the head: who you are talking to, and whether they are there ── */
.chat-head {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  /* Border full-bleed, words on the conversation's measure — the same
     rule the timeline follows under its header. */
  padding: 10px max(var(--s-4), calc((100% - var(--chat-measure)) / 2));
  border-bottom: 1px solid var(--line);
}
.chat-ident {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.chat-avatar {
  display: grid;
  place-items: center;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--surface-2);
  flex: 0 0 auto;
  overflow: hidden;
}
.chat-head-title {
  font-size: var(--text-base);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-tight);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-presence {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-2xs);
  color: var(--muted);
  white-space: nowrap;
}
.chat-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--muted);
  flex: 0 0 auto;
}
.chat-presence[data-state="on"] .chat-dot {
  background: var(--success);
  box-shadow: 0 0 0 3px rgba(21, 126, 60, 0.16);
}
.chat-presence[data-state="wait"] .chat-dot { background: var(--warning); }
.chat-presence[data-state="off"] .chat-dot { background: var(--danger); }
.chat-head-actions { display: flex; align-items: center; gap: var(--s-3); }
.chat-quiet {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
  padding: 4px 0;
  white-space: nowrap;
}
.chat-quiet:hover { text-decoration: underline; }
.chat-quiet-icon, .chat-quiet-short { display: none; }
#chat-info { color: var(--muted); }
#chat-info:hover, #chat-info[aria-expanded="true"] { color: var(--text); }
.chat-quiet[aria-expanded="true"] { color: var(--text); }

/* ── the panel: the session, said in numbers ─────────────────────── */
.chat-panel {
  width: 100%;
  /* The panel must not eat the conversation: it scrolls inside itself. */
  max-height: 55vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-4) max(var(--s-4), calc((100% - var(--chat-measure)) / 2));
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}
.chat-panel-grid {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 6px var(--s-4);
  font-size: var(--text-xs);
}
.chat-panel-key { color: var(--muted); }
.chat-panel-val { color: var(--text); font-variant-numeric: tabular-nums; }
.chat-panel-meter {
  height: 3px;
  margin: 10px 0 2px;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
}
.chat-panel-meter span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.3s ease;
}
.chat-panel-meter span[data-low="true"] { background: var(--danger); }
.chat-panel-note {
  margin: 8px 0 0;
  font-size: var(--text-2xs);
  color: var(--muted);
  max-width: 68ch;
}

/* ── the log ─────────────────────────────────────────────────────── */
.chat-log {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-4) 0;
  scrollbar-width: thin;
}
.chat-empty {
  display: grid;
  justify-items: start;
  gap: 10px;
  text-align: left;
  padding: 6vh var(--s-4) 0;
  max-width: var(--chat-measure);
  margin: 0 auto;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}
.chat-empty h1 { margin: 0; font-size: var(--text-xl); letter-spacing: var(--tracking-tight); }
.chat-empty p { margin: 0; max-width: 52ch; color: var(--muted); }
.chat-empty-hint { font-size: var(--text-2xs) !important; }
.chat-empty-mark { display: block; line-height: 0; opacity: 0.9; }
.chat-suggestions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 8px;
  margin: 14px auto 0;
  /* The chips wrap on the conversation's measure, not the screen's: the
     grid item otherwise keeps its max-content width and the last chip
     runs off the right edge. */
  width: 100%;
  min-width: 0;
}
.chat-suggestion {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  border-radius: 999px;
  padding: 8px 14px;
  font: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
}
.chat-suggestion:hover { border-color: var(--line-strong); background: var(--surface-2); }

.chat-msg { padding: 10px var(--s-5); max-width: var(--chat-measure); margin: 0 auto; width: 100%; }
.chat-who {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--muted);
  margin-bottom: 4px;
}
.chat-msg--user .chat-who { justify-content: flex-end; }
/* The agent's own face sits on its answers; the composer's frame keeps a
   plain circle so the drawing is the only thing that carries identity. */
.chat-avatar { width: 32px; height: 32px; background: none; }
.chat-at {
  margin-left: 8px;
  font-size: 12px;
  font-weight: var(--font-weight-normal);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.chat-body {
  overflow-wrap: anywhere;
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text);
}
.chat-body:empty { display: none; }
.chat-body p { margin: 0 0 0.7em; }
.chat-body p:last-child { margin-bottom: 0; }
.chat-body ul, .chat-body ol { margin: 0.3em 0 0.7em; padding-left: 1.3em; }
.chat-body li { margin: 0.15em 0; }
.chat-body h1, .chat-body h2 { margin: 0.5em 0 0.35em; font-size: var(--text-lg); letter-spacing: var(--tracking-tight); }
.chat-body h3, .chat-body h4, .chat-body h5, .chat-body h6 {
  margin: 0.5em 0 0.3em;
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-tight);
}
.chat-body hr { border: 0; border-top: 1px solid var(--line); margin: 0.9em 0; }
.chat-body pre {
  margin: 0.4em 0 0.7em;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-2);
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 1.45;
}
.chat-body pre code { background: none; padding: 0; }
.chat-body del { color: var(--muted); }
/* A table in an answer is data, not decoration: hairline rows, the way
   the cycles page prints its own tables. */
.chat-table {
  margin: 0.5em 0 0.8em;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.chat-body table {
  border-collapse: collapse;
  width: 100%;
  font-size: var(--text-xs);
}
/* A five-column table does not fit a phone, and squeezing it breaks
   words — "/art icle /89". On a narrow screen the table keeps its own
   width and scrolls inside its box; on the column's own measure it
   fills the width and wraps. */
@media (max-width: 760px) {
  .chat-body table { width: max-content; min-width: 100%; }
}
.chat-body th, .chat-body td {
  text-align: left;
  padding: 6px 10px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
  min-width: 7ch;
  max-width: 28ch;
}
.chat-body th { color: var(--muted); font-weight: var(--font-weight-semibold); }
.chat-body blockquote {
  margin: 0.3em 0 0.7em;
  padding-left: 12px;
  border-left: 2px solid var(--line-strong);
  color: var(--ink-soft);
}
.chat-body code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
}
.chat-body a { color: var(--accent); text-decoration: none; }
.chat-body a:hover { text-decoration: underline; }
/* Your own words sit on the right, the way a messenger puts them there:
   a conversation reads as two voices only if the turns look different. */
.chat-msg--user .chat-who { text-align: right; }
.chat-msg--user .chat-body {
  background: var(--surface-2);
  border-radius: 18px 18px 4px 18px;
  padding: 10px 14px;
  margin-left: auto;
  width: fit-content;
  max-width: 78%;
}
.chat-body.is-error { color: var(--danger); }
.chat-body.is-cancelled { color: var(--muted); }

/* What the turn is doing, while it does it. */
.chat-meta { margin-top: 2px; }
.chat-trace { margin: 0 0 8px; }
/* Once the answer is here, the steps become a receipt: one quiet line
   that opens when a reader wants it. */
.chat-trace--past summary {
  list-style: none;
  cursor: pointer;
  font-size: var(--text-2xs);
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
}
.chat-trace--past summary::-webkit-details-marker { display: none; }
.chat-trace--past summary::before {
  content: "▸";
  font-size: 9px;
  transform: translateY(1px);
}
.chat-trace--past[open] summary::before { content: "▾"; }
.chat-trace--past summary:hover { color: var(--text); }
.chat-trace--past .chat-steps {
  margin-top: 6px;
  padding-left: 10px;
  border-left: 1px solid var(--line);
}
.chat-steps { display: flex; flex-direction: column; gap: 3px; margin: 2px 0 4px; }
.chat-step {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: var(--text-2xs);
  color: var(--muted);
}
.chat-step::before {
  content: "";
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--line-strong);
  flex: 0 0 auto;
  transform: translateY(-1px);
}
.chat-step.is-live::before { background: var(--accent); animation: chat-pulse 1.1s ease-in-out infinite; }
.chat-step.is-done::before { background: var(--success); }
@keyframes chat-pulse { 50% { opacity: 0.25; } }

.chat-status {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-2xs);
  color: var(--muted);
}
.chat-status::before {
  content: "";
  width: 10px; height: 10px;
  border-radius: 50%;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  animation: chat-spin 0.8s linear infinite;
  flex: 0 0 auto;
}
@keyframes chat-spin { to { transform: rotate(360deg); } }

.chat-sources { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px; }
.chat-sources-word { font-size: var(--text-2xs); color: var(--muted); }
.chat-source {
  font-size: var(--text-2xs);
  color: var(--accent);
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 9px;
}
.chat-source:hover { border-color: var(--line-strong); }
.chat-usage {
  margin-top: 6px;
  font-size: var(--text-2xs);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.chat-actions {
  display: flex;
  gap: 6px;
  margin-top: 8px;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.chat-msg--bot:hover .chat-actions,
.chat-actions:focus-within { opacity: 1; }
@media (hover: none) { .chat-actions { opacity: 1; } }
.chat-action {
  appearance: none;
  border: 1px solid var(--line);
  background: none;
  color: var(--muted);
  border-radius: 999px;
  padding: 4px 10px;
  font: inherit;
  font-size: var(--text-2xs);
  cursor: pointer;
}
.chat-action:hover { color: var(--text); border-color: var(--line-strong); }

/* ── the composer ────────────────────────────────────────────────── */
.chat-composer {
  padding: var(--s-3) var(--s-4) 10px;
  background: var(--bg);
  padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
}
.chat-files {
  max-width: var(--chat-measure);
  margin: 0 auto 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.chat-file {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  padding: 5px 6px 5px 12px;
  font-size: var(--text-2xs);
  max-width: 100%;
}
.chat-file-thumb {
  width: 22px; height: 22px;
  border-radius: 6px;
  object-fit: cover;
  flex: 0 0 auto;
}
.chat-file-name { max-width: 24ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-file-size { color: var(--muted); white-space: nowrap; }
.chat-file.is-error { border-color: var(--danger); color: var(--danger); }
.chat-file.is-error .chat-file-size { color: var(--danger); }
.chat-file-drop {
  appearance: none;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}
.chat-field {
  max-width: var(--chat-measure);
  margin: 0 auto;
  display: flex;
  align-items: flex-end;
  gap: 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 6px 8px 6px 6px;
}
.chat-field:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
.chat.is-dragging .chat-field { border-color: var(--accent); border-style: dashed; }
.chat-field textarea {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: var(--text-sm);
  line-height: 1.45;
  resize: none;
  max-height: 180px;
  padding: 9px 0;
}
.chat-field textarea:focus { outline: none; }
.chat-attach {
  appearance: none;
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  cursor: pointer;
  flex: 0 0 auto;
}
.chat-attach:hover { color: var(--accent); background: var(--surface-2); }
.chat-attach:disabled { opacity: 0.4; cursor: default; }
.chat-count {
  font-size: var(--text-2xs);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  align-self: center;
  padding-right: 4px;
}
/* Same trap as `.icon-button`: the global `button` rule carries 16px of
   horizontal padding, which squeezes a 40px icon button's grid area to
   8px and leaves the icon 6px right of centre. A circle sizes itself. */
.chat-send {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  cursor: pointer;
}
.chat-send:disabled {
  background: var(--surface-2);
  color: var(--muted);
  cursor: default;
}
.chat-send .chat-icon-stop { display: none; }
.chat-send.is-stop { background: var(--text); color: var(--bg); }
.chat-send.is-stop .chat-icon-send { display: none; }
.chat-send.is-stop .chat-icon-stop { display: block; }
.chat-foot {
  max-width: var(--chat-measure);
  margin: 8px auto 0;
  text-align: center;
  color: var(--muted);
  font-size: var(--text-2xs);
}
.chat-foot-sep { padding: 0 4px; }

@keyframes chat-blink { to { visibility: hidden; } }
@media (prefers-reduced-motion: reduce) {
  .chat-status::before, .chat-step.is-live::before { animation: none; }
  .chat-panel-meter span { transition: none; }
}

@media (max-width: 760px) {
  .chat {
    height: calc(100dvh - var(--topbar-h, 96px) - 57px - env(safe-area-inset-bottom, 0px));
  }
  .chat-presence { display: none; }
  /* The name is the one thing in the head that must not be cut: the
     panel control becomes its icon instead. */
  .chat-quiet-icon, .chat-quiet-short { display: block; }
  .chat-quiet-text { display: none; }
  .chat-quiet { padding: 8px 4px; }
  .chat-head-title { font-size: var(--text-sm); }
  .chat-avatar { width: 28px; height: 28px; }
  .chat-head { padding: 8px var(--s-4); }
  .chat-head-actions { gap: var(--s-3); }
  .chat-msg { padding: 10px var(--s-4); }
  .chat-msg--user .chat-body { max-width: 88%; }
  .chat-panel { padding: var(--s-4); }
  .chat-panel-grid { grid-template-columns: 1fr; gap: 2px; }
  .chat-panel-key { margin-top: 6px; }
  /* A thumb is not a cursor: every control in the composer is 44px. */
  .chat-suggestion { padding: 11px 14px; min-height: 44px; }
  .chat-quiet { min-height: 40px; padding: 8px 0; }
  .chat-send, .chat-attach { width: 44px; height: 44px; }
  .chat-field { padding: 4px 6px 4px 4px; }
}

/* `hidden` is a UA rule and loses to an author `display: grid`; without
   this the welcome panel stayed on screen above the conversation. */
.chat-empty[hidden] { display: none; }
.chat-files[hidden] { display: none; }
.chat-panel[hidden] { display: none; }
.chat-sources[hidden] { display: none; }
