
:root {
  --bg: #0f1412; --panel: #161d1a; --panel-deep: #0f1412;
  --text: #e8efe9; --text-soft: #c7d2ca; --sub: #8fa096; --faint: #5c6b62;
  --green: #5ce0a1; --red: #e06c5c; --amber: #e0b25c;
  --border: rgba(255,255,255,.08); --border-strong: rgba(255,255,255,.13);
  --green-border: rgba(92,224,161,.4); --red-glow: rgba(224,108,92,.5);
  --hover: #1b2420;
  --font-ui: 'Space Grotesk', sans-serif; --font-mono: 'IBM Plex Mono', monospace;
}
* { box-sizing: border-box; margin: 0; }
body {
  background: var(--bg); color: var(--text); font-family: var(--font-ui);
  font-size: 14px; line-height: 1.55; -webkit-font-smoothing: antialiased;
}
a { color: var(--green); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Wordmark */
.wordmark { font-family: var(--font-mono); font-weight: 600; font-size: 16px; color: var(--text); }
.wordmark .cursor { color: var(--green); animation: blink 1.1s step-end infinite; }

/* Eyebrow / mono labels */
.eyebrow {
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--green);
}
.mono { font-family: var(--font-mono); }
.meta { font-size: 12.5px; color: var(--faint); }

/* Buttons */
.btn {
  display: inline-block; font-family: var(--font-mono); font-weight: 600; font-size: 13px;
  padding: 11px 18px; border-radius: 7px; border: 1px solid transparent;
  cursor: pointer; white-space: nowrap; text-decoration: none !important;
}
.btn-primary { background: var(--green); color: #0f1412; }
.btn-primary:hover { filter: brightness(1.08); }
.btn-secondary { background: transparent; border-color: var(--green-border); color: var(--green); }
.btn-secondary:hover { background: rgba(92,224,161,.08); }
.btn-ghost { background: transparent; border-color: rgba(255,255,255,.15); color: var(--sub); }
.btn-ghost:hover { color: var(--text-soft); }
.btn-danger { background: transparent; border-color: var(--red); color: var(--red); }
.btn-danger.armed { background: var(--red); color: #0f1412; }
.btn[disabled] { opacity: .5; cursor: default; pointer-events: none; }

/* Cards */
.card { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 22px 26px; }
.card h2 { font-size: 22px; margin-bottom: 8px; }
.card-title { font-size: 15px; font-weight: 600; margin-bottom: 10px; }

/* Severity badges */
.badge {
  font-family: var(--font-mono); font-weight: 600; font-size: 10px;
  letter-spacing: .06em; border-radius: 4px; padding: 3px 8px; text-transform: uppercase;
}
.badge-high, .badge-critical { background: rgba(224,108,92,.14); color: var(--red); }
.badge-medium { background: rgba(224,178,92,.14); color: var(--amber); }
.badge-low, .badge-info { background: rgba(255,255,255,.08); color: var(--sub); }
.badge-open { background: rgba(224,108,92,.14); color: var(--red); }
.badge-resolved { background: rgba(92,224,161,.14); color: var(--green); }
.badge-dismissed { background: rgba(255,255,255,.08); color: var(--sub); }
.badge-free { background: rgba(255,255,255,.08); color: var(--sub); }
.badge-mcp { background: rgba(92,224,161,.14); color: var(--green); }

/* LEDs */
.led { display: inline-block; width: 8px; height: 8px; border-radius: 99px; vertical-align: middle; }
.led-green { background: var(--green); animation: pulseDot 1.8s ease-out infinite; }
.led-red { background: var(--red); animation: pulseRed 1.8s ease-out infinite; }
.led-amber { background: var(--amber); animation: pulseAmber 1.8s ease-out infinite; }
.led-gray { background: var(--faint); }

/* Findings rows */
.frow {
  display: flex; align-items: center; gap: 12px; padding: 14px 16px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 9px; cursor: pointer;
}
.frow:hover { background: var(--hover); }
.frow.dismissed { opacity: .45; }
/* The severity glow follows the row into the table: same animation, same
   trigger, on whichever element is the row in that idiom. */
.frow.glow-high, details.wrowbox > summary.glow-high { animation: glowHigh 2.4s ease-in-out infinite; }
/* THE ROW'S THREE ZONES, and which one gives way when the row runs out of width.
 *
 * .frow-title { flex: 1 } alone means flex-basis 0 with no min-content floor,
 * while its sibling path (.mono.meta, one unbreakable token like
 * /pricing/enterprise-plans) cannot shrink at all. So the title took the whole
 * overflow and wrapped one word per line while the path sat there at full
 * width — the exact opposite of what is readable. The title is a short
 * sentence and stays on one line; the PATH is the part that ellipsizes (same
 * idiom as the "recently discovered" lines), and the stake caption on the right
 * is short, nowrap, and never shrinks. */
.frow-title {
  flex: 1; min-width: 8em; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* flex: 0 1 auto — it does NOT take the free space (the title still does, so
   the row looks exactly as it did when everything fits), but it absorbs all of
   the shrink, because min-width:0 is what lets a flex item go below its
   min-content size at all. */
.frow-path { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.frow-stake { flex: 0 0 auto; margin-left: auto; white-space: nowrap; }
/*
 * Expandable rows are <details>/<summary> (Batch D, keyboard access): a div with
 * an onclick handler is invisible to the keyboard and to assistive tech, and
 * these rows are the primary way anyone reads a finding. The marker is removed
 * because the row already carries its own ▾ affordance.
 */
details.frowbox > summary { list-style: none; }
details.frowbox > summary::-webkit-details-marker { display: none; }
details.frowbox > summary::marker { content: ''; }
details.frowbox[open] > summary .frow-caret { transform: rotate(180deg); display: inline-block; }
.frow-detail {
  background: var(--panel); border: 1px solid var(--border); border-top: 0;
  border-radius: 0 0 9px 9px; padding: 16px 18px 18px; margin-top: -6px;
}
/* ────────────────────────────────────────────────────────────────────────────
   THE WORK QUEUE AS A COLUMN-ALIGNED TABLE (redesign #work L695-712).

   The queue was a stack of free-flowing flex rows: a finding row put its path
   wherever the title left room, a proposal row put its field where the finding
   put its severity, and a change row put a date where both put a stake. Three
   idioms in one list means nothing can be COMPARED down a column — which is
   the entire reason the design draws a table here.

   ONE template, six tracks, shared by the header row and by every row from all
   three block renderers. The two flexible tracks are minmax(0, …) so a long
   title or a long path shrinks instead of blowing the grid out past its
   container (the classic plain-1fr min-content trap).

   THE EXPANDER SURVIVES. Each row is still a <details>: the SUMMARY is the grid
   row, and the detail opens BELOW it at full width, outside the tracks. That is
   the one deliberate departure from the design mock, which has no expansion at
   all — every per-row action (draft, dismiss, approve, reject, revert, edit,
   og-picker) lives in that detail and has nowhere else to go.

   "display:block" on the <summary> with the grid on an inner span, rather than
   grid on the summary itself: WebKit has historically dropped the disclosure
   behaviour when a summary's display is changed, and the toggle is the one
   thing on this row that must not be clever. */
.worktable { --work-cols: 26px 58px minmax(0, 2.1fr) minmax(0, 1.35fr) 104px 108px; }
.whead, .wrow { display: grid; grid-template-columns: var(--work-cols); gap: 9px; align-items: center; }
.whead {
  padding: 9px 14px; border-bottom: 1px solid var(--border);
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .08em; color: var(--sub);
}
details.wrowbox { border-bottom: 1px solid var(--border); }
details.wrowbox:last-child { border-bottom: 0; }
details.wrowbox > summary { display: block; list-style: none; cursor: pointer; padding: 9px 14px; }
details.wrowbox > summary::-webkit-details-marker { display: none; }
details.wrowbox > summary::marker { content: ''; }
details.wrowbox > summary:hover { background: var(--hover); }
details.wrowbox[open] > summary { background: var(--hover); }
details.wrowbox[open] > summary .frow-caret { transform: rotate(180deg); display: inline-block; }
details.wrowbox.dismissed > summary { opacity: .45; }
/* The detail is a full-width panel under the row, not a seventh cell: it drops
   the .frow-detail rounded corners and border, which belonged to a free-standing
   row rather than to a band inside a table. */
details.wrowbox > .frow-detail { border: 0; border-top: 1px solid var(--border); border-radius: 0; margin-top: 0; }
/* The title cell: a sentence over a caption, and the cell may shrink. */
.wcell-title { min-width: 0; }
.wcell-title .wtitle {
  display: block; font-size: 13px; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wcell-title .wstake { display: block; font-family: var(--font-mono); font-size: 10.5px; line-height: 1.45; margin-top: 2px; color: var(--faint); }
/* Every mono cell that carries one unbreakable token (a path) ellipsizes rather
   than widening its track. */
.wcell-mono { font-family: var(--font-mono); font-size: 11.5px; color: var(--sub); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A full-width band inside the table — the progressive-reveal toggle and the
   truncation notes, which are about the LIST rather than about a row. */
.wspan { padding: 11px 14px; border-bottom: 1px solid var(--border); }
.wspan:last-child { border-bottom: 0; }
.fixbox {
  border: 1px solid var(--green-border); border-radius: 8px; padding: 12px 14px;
  margin: 12px 0; font-size: 13.5px; color: var(--text-soft);
}
.fixbox .fixlabel { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .08em; color: var(--green); display: block; margin-bottom: 5px; }
/* A REFUSED DRAFT, SAID WHERE THE DRAFT WAS ASKED FOR.
 *
 * This used to be one bare red mono line pinned to the top of the page —
 * disconnected from the finding whose button produced it, in a typeface that
 * made a sentence written for a human read like a stack trace. It is the same
 * shape as .fixbox (the row's other "here is what to do about it" box), in the
 * UI font, with the red reserved for the label and the left edge. */
.blockbox {
  background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--red);
  border-radius: 8px; padding: 12px 14px; margin: 0 0 12px; font-size: 13.5px; color: var(--text-soft);
}
.blockbox .blocklabel { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .08em; color: var(--red); display: block; margin-bottom: 5px; }
/* EDIT THIS DRAFT — the inline editor on a proposal row.
 *
 * A <details>, like every other expandable thing in this app, so it works with
 * no JavaScript and is reachable from the keyboard. The summary is a mono label
 * rather than a button: it opens an editor, it does not perform an action, and
 * dressing it as a third button beside Approve/Reject would put "start typing"
 * at the same visual weight as "publish this to the edge". */
.editbox { border-top: 1px dashed var(--border); padding-top: 10px; }
.editsummary {
  font-family: var(--font-mono); font-size: 11.5px; letter-spacing: .06em;
  color: var(--sub); cursor: pointer; list-style: none; display: inline-block;
}
.editsummary::-webkit-details-marker { display: none; }
.editsummary::before { content: '✎ '; color: var(--faint); }
.editsummary:hover { color: var(--text); }
.editbox[open] .editsummary { color: var(--text); }
/* Save acknowledgement — the "we heard you" chip (L1).
   Query-param driven, so it cannot outlive the redirect that produced it. The
   fade is PURELY VISUAL: the element stays in the DOM (and in the a11y tree,
   role="status") after the animation, so tests and screen readers still see it.
   animation-fill-mode: forwards leaves it at the final opacity without JS. */
.savedchip {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono); font-size: 12px; line-height: 1.4;
  color: var(--green); background: rgba(92,224,161,.12);
  border: 1px solid var(--green-border); border-radius: 6px;
  padding: 4px 10px; margin: 0;
  animation: savedFade 4s ease-out forwards;
}
/* THE SAVE ROW — the button and its acknowledgement, in one element.
   Alan, on the chip landing at the card top: "the user is on the button, they
   hit it, it should say what the action did, not jump away." So the chip
   renders INSIDE this row, beside the button that produced it.
   Since the settings tab conversion, most saves land via ?tab= with no anchor
   at all — the tab render itself puts the card on screen, nothing to scroll
   to. scroll-margin-top now only serves the handful of anchors the tab pages
   still carry alongside ?tab=: the below-fold Knowledge cards (facts,
   vocabulary, titles) and General's danger card, whose redirects and inbound
   links (incl. the hash shim's landing) still point at #<section>-save.
   scroll-margin-top is the other half of those: an anchor pins its target to
   the very top of the viewport, which is nowhere near where a clicking user's
   eye was. 40vh drops the row into the middle of the screen instead —
   roughly where the button already was when they pressed it, so the page
   barely appears to move. */
.saverow {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  scroll-margin-top: 40vh;
}
.savedchip .savedchip-next { color: var(--text-soft); }
.savedchip .savedchip-next a { color: var(--green); }
@keyframes savedFade { 0%, 70% { opacity: 1; } 100% { opacity: .28; } }
/* Motion-sensitive users get the chip, permanently, with no movement at all. */
@media (prefers-reduced-motion: reduce) { .savedchip { animation: none; } }
/* P7 — the queued-saves counter, top right, above everything.
   Same chip family as .savedchip (mono, 12px, soft-bordered pill) in the
   neutral/amber register rather than green: nothing has been saved yet, it is
   still happening. The ONLY position:fixed element in this stylesheet, so the
   z-index has nothing to compete with; 20 is stated anyway so a future overlay
   has an obvious number to beat.
   THE OFFSET CLEARS THE PAGE HEADER. .main is padded 26px 34px and opens with
   .pagehead (a ~34px control row, margin-bottom 22px) whose right end holds the
   run-audit button on the pages that have one. 74px starts this below that row
   and 34px lines it up with the content's right edge, so it never sits on top
   of a header control on any page that later renders one.
   No animation at all: a counter that pulses is a counter that fights
   prefers-reduced-motion for no information gained. */
.savequeue {
  position: fixed; top: 74px; right: 34px; z-index: 20;
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono); font-size: 12px; line-height: 1.4;
  color: var(--amber); background: rgba(224,178,92,.14);
  border: 1px solid rgba(224,178,92,.34); border-radius: 6px;
  padding: 4px 10px;
}
/* display:inline-flex would otherwise beat the hidden attribute outright, and
   the server renders this element hidden. */
.savequeue[hidden] { display: none; }
.pathchip {
  font-family: var(--font-mono); font-size: 11.5px; color: var(--text-soft);
  background: var(--panel-deep); border: 1px solid var(--border); border-radius: 5px;
  padding: 2px 8px; display: inline-block; margin: 2px 3px 2px 0;
}

/* Filter chips */
.chip {
  font-size: 12.5px; border-radius: 99px; padding: 5px 13px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.15); color: var(--sub); background: transparent;
  font-family: var(--font-ui);
}
.chip.active { background: var(--text); color: #0f1412; border-color: var(--text); }

/* Section tabs (settings).
   Derived from .chip on purpose: the app already navigates with pill-shaped
   filters, and a tab is the same affordance carrying a page-level selection
   instead of a list filter. The row wraps rather than scrolls — a narrow window
   gets two lines of tabs, never a hidden sixth one. */
.tabbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.tabbar .tab {
  font-size: 12.5px; border-radius: 99px; padding: 6px 15px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.15); color: var(--sub); background: transparent;
  font-family: var(--font-ui); text-decoration: none !important; white-space: nowrap;
}
.tabbar .tab:hover { color: var(--text-soft); border-color: rgba(255,255,255,.28); }
.tabbar .tab.active { background: var(--text); color: #0f1412; border-color: var(--text); }

/* The UNDERLINED TAB RAIL (redesign #settings).
   A pill row and an underlined rail say two different things: a pill is a
   filter you can turn off, an underlined rail is "you are in one of these
   places, always". Settings is the second, and the design draws it as a
   border-bottom rail with the active tab carrying a 2px green underline that
   sits ON the rail (margin-bottom:-1px) rather than floating above it — the
   same idiom as the census filter row's underlined word.

   ".tabbar" above is deliberately left intact: it is the generic pill row, and
   a future consumer that wants a filter-shaped tab should still get one. */
.tabrail { display: flex; gap: 0; flex-wrap: wrap; align-items: stretch; border-bottom: 1px solid var(--border); }
.tabrail .tab {
  font-size: 13.5px; padding: 9px 16px; cursor: pointer; color: var(--sub);
  background: transparent; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px;
  font-family: var(--font-ui); text-decoration: none !important; white-space: nowrap;
}
.tabrail .tab:hover { color: var(--text-soft); }
.tabrail .tab.active { color: var(--green); font-weight: 600; border-bottom-color: var(--green); }

/* The settings card grid (redesign #settings). Two columns at desktop, one
   below 900px — the same breakpoint the fixed-track tables give up at. A card
   marked ".full" spans the row: the danger zone is the case this exists for,
   because a destructive action must never sit shoulder to shoulder with a
   routine one and read as its equal. */
/* ONE FULL-WIDTH STACK — every settings tab (2026-08-10). The two-column
   multicol (.cardgrid) that lived here half-filled short tabs and opened
   balancing gaps above odd cards out; the cards are editing surfaces and
   want the whole page. Flex gap, not margins, so the gap between every
   pair of cards is exactly one value. */
.cardstack { display: flex; flex-direction: column; gap: 15px; }

/* Terminal log */
.term {
  font-family: var(--font-mono); font-size: 12.5px; line-height: 1.95;
  background: rgba(22,29,26,.85); border: 1px solid var(--border); border-radius: 10px;
  padding: 16px 20px; color: var(--text-soft);
}
.term .ok { color: var(--green); } .term .bad { color: var(--red); } .term .warn { color: var(--amber); }
.term .cursor { color: var(--green); animation: blink 1.1s step-end infinite; }
.term .line { opacity: 0; animation: riseIn .25s ease forwards; }

/* Score gauge */
.gauge { position: relative; width: 108px; height: 108px; border-radius: 99px;
  background: conic-gradient(var(--green) calc(var(--score) * 1%), var(--border) 0); }
.gauge::before { content: ''; position: absolute; inset: 9px; border-radius: 99px; background: var(--panel); }
.gauge .num { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; font-weight: 600; font-size: 28px; }
.gauge .num small { font-family: var(--font-mono); font-size: 10.5px; color: var(--faint); font-weight: 400; }

/* App shell */
.shell { display: flex; min-height: 100vh; }
.sidebar {
  width: 218px; flex-shrink: 0; border-right: 1px solid var(--border);
  padding: 22px 14px; display: flex; flex-direction: column; gap: 4px;
}
.sidebar .navlink {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 8px;
  color: var(--sub); font-size: 13.5px; text-decoration: none !important;
}
.sidebar .navlink:hover { background: var(--hover); color: var(--text-soft); }
.sidebar .navlink.active { background: var(--panel); color: var(--text); }
.sidebar .navlink.active .icon { color: var(--green); }
/* The two group labels (MEASURE / WORK). Not links, no state — a quiet axis
   marker over the entries beneath it. Sized and coloured like the app's other
   mono eyebrows so it reads as a caption, never as a disabled nav entry. */
.sidebar .navgroup {
  padding: 14px 12px 4px; font-size: 10.5px; letter-spacing: .1em; color: var(--faint);
}
.sidebar .count { margin-left: auto; background: rgba(224,108,92,.14); color: var(--red);
  font-family: var(--font-mono); font-size: 10.5px; border-radius: 99px; padding: 1px 7px; }
.main { flex: 1; min-width: 0; padding: 26px 34px; }
/* position:relative anchors the two P8 header panels (jobs, notifications),
   which hang off the right edge of this row. */
.pagehead { display: flex; align-items: center; gap: 16px; margin-bottom: 22px; position: relative; }
.pagehead h1 { font-size: 20px; }
.pagehead .status { font-family: var(--font-mono); font-size: 12px; color: var(--faint); }
.pagehead .spacer { flex: 1; }
/* ------------------------------------------------- P8 header menus
 * The jobs pill and the notification bell. Both are <details> — no JavaScript,
 * keyboard-reachable, and opening one cannot change any state on the server.
 * The panel is absolutely positioned so opening it does not reflow the page
 * under the reader's cursor. */
.hdrmenu { position: static; }
.hdrmenu > summary {
  list-style: none; cursor: pointer; white-space: nowrap;
  font-family: var(--font-mono); font-size: 12px; color: var(--text-soft);
  border: 1px solid var(--border-strong); border-radius: 7px; padding: 6px 12px;
}
.hdrmenu > summary::-webkit-details-marker { display: none; }
.hdrmenu > summary::marker { content: ''; }
.hdrmenu > summary:hover { border-color: rgba(255,255,255,.28); color: var(--text); }
/* A HEADER LINK, AS A CHIP (redesign #overview L212-216: "share report ▾" is a
   bordered box beside the green run-now box, not a bare mono word). Exactly the
   .hdrmenu summary treatment above, in the existing border token rather than the
   mock's raw rgba — one header, one chip idiom, so the two controls beside each
   other cannot end up different heights. Only the neutral one: the run-now
   control keeps its green border, which is what says it is the primary action. */
.hdrlink {
  font-family: var(--font-mono); font-size: 12px; color: var(--text-soft);
  border: 1px solid var(--border-strong); border-radius: 7px; padding: 6px 12px;
  white-space: nowrap; text-decoration: none;
}
.hdrlink:hover { border-color: rgba(255,255,255,.28); color: var(--text); text-decoration: none; }
.jobspill > summary { color: var(--green); border-color: var(--green-border); }
.jobspill .spin { display: inline-block; animation: spin 1.1s linear infinite; }
@media (prefers-reduced-motion: reduce) { .jobspill .spin { animation: none; } }
.bellcount {
  margin-left: 7px; font-size: 11px; color: var(--amber);
}
.hdrpanel {
  position: absolute; top: 100%; right: 0; margin-top: 8px; width: 340px; max-width: 92vw;
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: 11px;
  padding: 14px; z-index: 12; box-shadow: 0 18px 44px rgba(0,0,0,.6);
  display: flex; flex-direction: column; gap: 2px;
}
.hdrpanel-wide { width: 380px; }
.hdrpanel-title { font-size: 10.5px; letter-spacing: .1em; color: var(--faint); }
.hdrrow {
  display: flex; gap: 10px; align-items: flex-start; padding: 9px 10px; border-radius: 8px;
  text-decoration: none !important; color: var(--text);
}
.hdrrow:hover { background: var(--hover); }
.hdrrow.unseen { background: rgba(92,224,161,.05); }
.hdrrow .hdrrow-text { display: block; font-size: 12.5px; color: var(--text); line-height: 1.45; }
.hdrrow .meta { display: block; margin-top: 3px; font-family: var(--font-mono); font-size: 10.5px; }
.hdrpanel-foot { margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--border); line-height: 1.6; }
.notedot { width: 6px; height: 6px; border-radius: 99px; flex: none; margin-top: 7px; background: var(--faint); }
.notedot.note-alert { background: var(--red); }
.notedot.note-auto-published { background: var(--green); }
.notedot.note-info { background: var(--sub); }
/* A submit button that has to read as a link — the mark-read control is a POST
   (state changes never ride a GET) but it is not a page-level action. */
.linkbtn {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: 11px; color: var(--green);
}
.linkbtn:hover { text-decoration: underline; }
.statgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 15px; }
/* THREE, not four (redesign #overview L212): a bot card is a name, a status
   glyph and a sentence of prose, and at a quarter of the main column the
   sentence broke to four words a line. The board also renders at most a
   handful of rows now (it is conditional on something being blocked), so the
   fourth track was buying nothing but narrower prose. */
.botgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-bottom: 15px; }
.twocol { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; }
/* THE OVERVIEW'S PAIRED ROW (redesign #overview: "// SERVING RIGHT NOW" beside
   "// DO THESE FIRST"). Deliberately unequal, like .portgrid and for the same
   reason: the serving card carries badged rows with a provenance line under
   each and a paragraph of copy, the do-these-first card carries ten short
   titles. 1fr/1fr crushed the left one. It collapses to one column at 900px —
   below that the ranked list stacking under the serving card is the honest
   reading order, since the ranked list is the one you act on. */
.pairgrid { display: grid; grid-template-columns: 1.5fr 1fr; gap: 15px; align-items: start; margin-bottom: 15px; }
/* P9 — THE TWO OTHER RATIOS THE DESIGN ASKS FOR, as classes and NOT as inline
   grid-template-columns. An inline style beats a media-query rule, so a card
   grid pinned inline never collapses on a phone — it just gets two crushed
   columns. Every ratio in the product is a class for that reason, and all of
   them are in the collapse rule below.
     .pairgrid-125 — Traffic/AI: crawler board beside served + baseline + usage.
     .pairgrid-115 — Traffic/Search (engines beside queries) and Citations
                     (suggestions, which carry a reason line each, beside the
                     uncited list, which carries short titles). */
.pairgrid-125 { grid-template-columns: 1.25fr 1fr; }
.pairgrid-115 { grid-template-columns: 1.15fr 1fr; }
.narrow { max-width: 760px; margin: 0 auto; }
/* THE PORTFOLIO'S TOP ROW (spec §4 — the prototype's All-sites frame): the
   pooled meter on the left, "which site spent it" on the right. Its own class
   rather than .twocol because the two cards are deliberately UNEQUAL — the
   meter carries three bars and their refusal sentences, the breakdown carries a
   four-column table — and 1fr/1fr crushed the meter's copy to four words a
   line. It collapses to one column in the narrow-viewport block below. */
.portgrid { display: grid; grid-template-columns: 1.35fr 1fr; gap: 15px; align-items: start; margin-bottom: 15px; }
/* The dense site table's rows. A whole row is one <a> (the prototype's "click
   any row to open it"), so the hover state belongs to the row, not to a title
   inside it — and there is no nested link anywhere in a row to steal the click. */
.prow { display: grid; gap: 10px; padding: 11px 18px; border-top: 1px solid var(--border);
  align-items: center; font-size: 13px; text-decoration: none !important; color: var(--text); }
.prow:hover { background: var(--hover); }
.phead { display: grid; gap: 10px; padding: 10px 18px; font-family: var(--font-mono);
  font-size: 10.5px; letter-spacing: .08em; color: var(--faint); }
/* One tab's worth of settings cards. Wider than .narrow because a tab holds a
   handful of cards rather than eleven, and the forms inside them (fact rows,
   term rows, the autopilot ladder) were the things 760px squeezed. */
.wide { max-width: 1100px; margin: 0 auto; }

/* Marketing */
.mkt { max-width: 1080px; margin: 0 auto; padding: 0 32px; }
.blueprint {
  background-image:
    repeating-linear-gradient(0deg, rgba(92,224,161,.038) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(90deg, rgba(92,224,161,.038) 0 1px, transparent 1px 32px);
}
.mkt-section { padding: 64px 0; border-bottom: 1px solid var(--border); }
.mkt-section.tint { background: rgba(22,29,26,.4); }
.hero h1 { font-size: 52px; line-height: 1.08; font-weight: 700; margin: 14px 0 16px; }
.hero h1 em { color: var(--green); font-style: normal; }
.hero .sub { font-size: 16px; color: var(--sub); max-width: 560px; }
.urlrow { display: flex; gap: 10px; margin: 26px 0 8px; max-width: 560px; }
.urlrow input {
  flex: 1; background: var(--panel); border: 1px solid var(--border-strong); border-radius: 7px;
  color: var(--text); font-family: var(--font-mono); font-size: 13.5px; padding: 11px 14px; outline: none;
}
.urlrow input:focus { border-color: var(--green-border); }
.howgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }
.finding-strip { border: 1px solid var(--red-glow); border-radius: 10px; padding: 18px 22px; margin-top: 26px; animation: glowHigh 2.4s ease-in-out infinite; }
.pricegrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }
.price .amount { font-size: 30px; font-weight: 700; margin: 6px 0; }
.price ul { list-style: none; padding: 0; margin: 12px 0; color: var(--sub); font-size: 13.5px; }
.price li { padding: 3px 0; }
.popular { border-color: var(--green-border); position: relative; }
.popular .tag { position: absolute; top: -9px; left: 20px; background: var(--green); color: #0f1412;
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 600; letter-spacing: .07em; border-radius: 4px; padding: 2px 8px; }
.footer { padding: 34px 0 44px; color: var(--faint); font-size: 12.5px; display: flex; gap: 20px; align-items: center; }

/* ------------------------------------------------------------------ Articles
 * Scoped under .article / .artlist ONLY. Nothing here restyles a bare h2, p or
 * ul, because those elements are used on every other marketing page and a
 * reading-layout rule leaking out of this section would resize half the site.
 *
 * One measure, one rhythm: ~68 characters at 16.5px is the readable column, and
 * the vertical spacing is set once here rather than per-article, so an author
 * writing a new module writes semantic markup and gets the typography free. */
.artlist { display: flex; flex-direction: column; gap: 10px; }
.artrow {
  display: block; padding: 16px 18px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--panel); text-decoration: none !important; color: var(--text);
}
.artrow:hover { background: var(--hover); border-color: var(--border-strong); }
.artrow-title { display: block; font-weight: 600; font-size: 15.5px; color: var(--text); }
.artrow-desc { display: block; font-size: 13.5px; color: var(--sub); margin-top: 5px; }
.artrow-date { display: block; font-size: 11.5px; color: var(--faint); margin-top: 8px; }

.article { max-width: 44rem; margin: 0 auto; padding: 40px 0 24px; }
.article h1 { font-size: 38px; line-height: 1.14; font-weight: 700; margin: 12px 0 14px; }
.article .article-standfirst { font-size: 17px; line-height: 1.6; color: var(--text-soft); margin-bottom: 14px; }
.article .article-dateline { padding-bottom: 20px; border-bottom: 1px solid var(--border); }
.article-body { font-size: 16.5px; line-height: 1.72; color: var(--text-soft); }
.article-body h2 {
  font-size: 22px; line-height: 1.25; font-weight: 600; color: var(--text);
  margin: 38px 0 12px;
}
.article-body p { margin: 0 0 18px; }
/* LISTS. The markup was already semantic and the list still read as one grey
 * paragraph: at 16.5px/1.72 in --text-soft, a default disc at 22px of indent is
 * a dot the eye does not separate from the prose above it, and 10px between
 * items is less than the 18px between paragraphs — so a four-item list looked
 * TIGHTER than the text around it and ran together.
 *
 * The fix is three things, none of them clever: give the item room (more than
 * the paragraph rhythm inside the list, so the items read as siblings), pull
 * the marker out of the text column and colour it with the accent so it is a
 * mark rather than a speck, and set the list-style explicitly rather than
 * inheriting whatever the UA decides — including for nested lists, where a
 * second level of identical discs is unreadable. Nothing here leaks past
 * .article-body; the rest of the site's uls are untouched. */
.article-body ul, .article-body ol { margin: 0 0 24px; padding-left: 26px; }
.article-body ul { list-style: disc; }
.article-body ol { list-style: decimal; }
.article-body ul ul { list-style: circle; }
.article-body ol ol { list-style: lower-alpha; }
.article-body ul ul, .article-body ol ol, .article-body ul ol, .article-body ol ul {
  margin: 10px 0 4px;
}
.article-body li { margin: 0 0 14px; padding-left: 4px; }
.article-body li:last-child { margin-bottom: 0; }
.article-body li::marker { color: var(--green); font-weight: 600; }
.article-body ol > li::marker { font-family: var(--font-mono); font-size: .9em; }
/* An item that opens with a bolded lead-in is the shape most of these lists
 * use; give the lead-in the body colour so the item has a visible head. */
.article-body li > b:first-child { color: var(--text); }

/* DEFINITION LISTS. Term-and-meaning pairs were being written as prose or as
 * a ul with an em-dash, which is the same run-together problem wearing list
 * markup. dl is the honest element for them, so it is styled to be worth
 * reaching for: the term sits on its own line in the body colour, the meaning
 * is indented under it, and neither carries a marker. */
.article-body dl { margin: 0 0 24px; }
.article-body dt { color: var(--text); font-weight: 600; margin: 0 0 4px; }
.article-body dd { margin: 0 0 16px; padding-left: 18px; border-left: 2px solid var(--border); }
.article-body dd:last-child { margin-bottom: 0; }
.article-body b { color: var(--text); font-weight: 600; }
.article-body em { color: var(--text); font-style: italic; }
.article-body a { text-decoration: underline; }
.article-body .mono { font-size: 15px; color: var(--text); }
@media (max-width: 700px) {
  .article h1 { font-size: 30px; }
  .article-body { font-size: 16px; }
}

/* Inputs / forms */
input[type=text], input[type=url], input[type=password] {
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: 7px;
  color: var(--text); font-family: var(--font-mono); font-size: 13.5px; padding: 10px 13px; outline: none; width: 100%;
}
input:focus { border-color: var(--green-border); }
.copyblock {
  background: var(--panel-deep); border: 1px solid var(--border); border-radius: 8px;
  font-family: var(--font-mono); font-size: 12px; padding: 12px 14px; word-break: break-all; position: relative;
}
/* Markdown-twin preview (markdown-preview.ts). Scoped under .mdprev so the
   generic copyblock stays untouched. Sized a step down from the app chrome —
   it is a document inside a review card, not the page itself. */
.mdprev { font-family: var(--font-ui); font-size: 13.5px; line-height: 1.6; color: var(--text-soft); }
.mdprev .mdprev-h1 { font-size: 17px; margin: 10px 0 6px; color: var(--text); }
.mdprev .mdprev-h2 { font-size: 15px; margin: 10px 0 5px; color: var(--text); }
.mdprev .mdprev-h3, .mdprev .mdprev-h4 { font-size: 13.5px; margin: 8px 0 4px; color: var(--text); }
.mdprev .mdprev-p { margin: 6px 0; }
.mdprev .mdprev-list { margin: 6px 0 6px 18px; }
.mdprev .mdprev-list li { margin: 3px 0; }
.mdprev .mdprev-code { font-family: var(--font-mono); font-size: 12px; background: var(--panel-deep); border: 1px solid var(--border); border-radius: 4px; padding: 8px 10px; margin: 8px 0; overflow-x: auto; white-space: pre; }
.mdprev code { font-family: var(--font-mono); font-size: 12px; background: var(--panel-deep); padding: 1px 4px; border-radius: 3px; }
.mdprev a { color: var(--green); }
.mdprev .mdprev-hr { border: 0; border-top: 1px solid var(--border); margin: 10px 0; }
.copybtn { position: absolute; top: 8px; right: 8px; }

/* Onboarding */
.wizard { display: flex; min-height: 100vh; align-items: stretch; }
.stepper { width: 250px; padding: 40px 30px; border-right: 1px solid var(--border); }
.step { display: flex; gap: 12px; align-items: baseline; padding: 10px 0; color: var(--faint); }
.step.active { color: var(--text); }
.step.done { color: var(--sub); }
.stepnum { font-family: var(--font-mono); font-size: 11px; border: 1px solid var(--border-strong);
  border-radius: 99px; width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.step.active .stepnum { border-color: var(--green); color: var(--green); }
.step.done .stepnum { background: rgba(92,224,161,.14); border-color: transparent; color: var(--green); }
.wizmain { flex: 1; display: flex; align-items: center; justify-content: center; padding: 40px; }
.wizcard { width: 520px; }
/* P9 — THE WIZARD IS A PAGE, NOT A RAIL PLUS A PANE. The step indicator became
   a horizontal strip across the top (design #wizard L1404-1417), which gives
   the 250px the left rail was spending on six words back to the step. The cap
   is 1100px for the five prose steps and is widened inline to 1240px on the
   edge step, the one screen the design draws with a rail of its own. */
.wizpage { max-width: 1100px; margin: 0 auto; padding: 34px 26px 48px; }
.stepstrip { display: flex; align-items: center; flex-wrap: wrap; row-gap: 10px; margin-bottom: 22px; }
/* The edge step's two columns: the flow, then the rail (design #wizard L1417).
   It collapses main-flow-first, because the rail is context for the work and
   never a precondition for it. */
.edgegrid { display: grid; grid-template-columns: 1.5fr 1fr; gap: 20px; align-items: start; }

/* Motion */
@keyframes blink { 50% { opacity: 0; } }
@keyframes riseIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes pulseDot { 0% { box-shadow: 0 0 0 0 rgba(92,224,161,.5); } 100% { box-shadow: 0 0 0 9px rgba(92,224,161,0); } }
@keyframes pulseRed { 0% { box-shadow: 0 0 0 0 rgba(224,108,92,.5); } 100% { box-shadow: 0 0 0 9px rgba(224,108,92,0); } }
@keyframes pulseAmber { 0% { box-shadow: 0 0 0 0 rgba(224,178,92,.5); } 100% { box-shadow: 0 0 0 9px rgba(224,178,92,0); } }
@keyframes glowHigh { 0%,100% { border-color: rgba(224,108,92,.35); } 50% { border-color: rgba(224,108,92,.9); } }
@keyframes spin { to { transform: rotate(360deg); } }
.riseIn { animation: riseIn .25s ease; }

/* ==========================================================================
 * NARROW VIEWPORT (spec §13.5 / §15.3, P8)
 * ==========================================================================
 * The prototype draws mobile frames for THREE screens — Overview, Citations and
 * Work — and says the rest "fall back". This is that fallback, made explicit
 * rather than left to chance:
 *
 *   1. THE SHELL COLLAPSES. The 218px sidebar becomes a horizontal, scrollable
 *      strip above the content. CSS only: no drawer, no toggle, no JavaScript,
 *      and every nav entry stays reachable — a hamburger that needs a script is
 *      a nav that disappears when the script doesn't load.
 *   2. THE GRIDS WRAP. Four-column stat and bot grids become two columns, then
 *      one. Two is the prototype's Overview frame; one is for a phone held in a
 *      hand, where two 26px numbers side by side stop being readable.
 *   3. NOTHING SCROLLS SIDEWAYS. The three row idioms in this app (.frow, the
 *      census/citation rows, the header panels) either wrap or scroll INSIDE
 *      their own box. ".wrapx" is the readable fallback §15.3 describes for the
 *      wide tables that genuinely cannot reflow — the six-engine citation grid
 *      is the one that needs it — and the page body never scrolls with them.
 *
 * This is not a redesign. Every rule below only changes layout: no colour and no
 * copy changes, and NO FACT disappears at any width — every number, sentence and
 * nav destination on screen at 1400px is on screen at 360px.
 *
 * THE ONE EXCEPTION IS LABELS, NOT FACTS (F5): the two ".sidebar .navgroup"
 * captions ("MEASURE" / "WORK") are hidden below 900px, along with the flex
 * spacers that position them. They caption a vertical column and mean nothing
 * in a horizontal strip; the seven nav entries they caption all stay. That is
 * the whole allowlist, and a test pins it — any other "display: none" inside a
 * media query fails the suite, because "a fact that was on screen yesterday" is
 * exactly what a width-conditional hide is most likely to take away. */
@media (max-width: 900px) {
  .shell { display: block; min-height: 0; }
  .sidebar {
    width: auto; flex-shrink: 1; border-right: 0; border-bottom: 1px solid var(--border);
    padding: 14px 16px; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 6px;
  }
  /* The two spacers that push Account/Admin to the bottom of a column mean
     nothing in a row, and the group captions are a vertical device. */
  .sidebar > div[style*="flex:1"] { display: none; }
  .sidebar .navgroup { display: none; }
  .sidebar .navlink { padding: 7px 10px; font-size: 12.5px; }
  .sidebar .siteswitch { margin-bottom: 0 !important; width: 100%; }
  .main { padding: 18px 16px 28px; }
  .pagehead { flex-wrap: wrap; gap: 10px; }
  .pagehead h1 { font-size: 18px; }
  .statgrid, .botgrid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .twocol, .howgrid, .pricegrid, .portgrid, .pairgrid,
  .pairgrid-125, .pairgrid-115, .edgegrid { grid-template-columns: 1fr; }
  .card { padding: 16px 18px; }
  /* WORK ROWS STACK (prototype mobile frame): the severity badge and title on
     one line, path and stake beneath, the action full width under both. */
  .frow { flex-wrap: wrap; padding: 12px 13px; gap: 8px; }
  .frow-title { flex: 1 1 100%; min-width: 0; white-space: normal; }
  .frow-path { flex: 1 1 auto; }
  .frow-stake { margin-left: 0; white-space: normal; }
  .hdrpanel, .hdrpanel-wide { width: min(340px, calc(100vw - 32px)); }
  .wizard { display: block; }
  .stepper { width: auto; padding: 22px 18px; border-right: 0; border-bottom: 1px solid var(--border); }
  .wizmain { padding: 22px 16px; }
  .wizcard { width: 100%; }
  .wizpage { padding: 22px 16px 34px; }
  .edgegrid { grid-template-columns: 1fr; }
  .savequeue { top: auto; bottom: 14px; right: 14px; }
}
@media (max-width: 560px) {
  .statgrid, .botgrid { grid-template-columns: 1fr; }
  .gauge { width: 92px; height: 92px; }
  .pagehead .status { flex: 1 1 100%; }
}
/* THE READABLE FALLBACK for content that genuinely cannot reflow — the six
   engine columns on Citations are the case this exists for. It scrolls inside
   its own box at every width, so the PAGE never scrolls sideways: a horizontal
   scrollbar on <body> hides content off an edge nobody thinks to look at. */
.wrapx { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
/* The three CSS-grid tables (Pages census, Citations engine grid, Content twin
   pipeline). Their column templates are fixed pixel tracks by design — the
   numbers have to line up down the column — so below about 900px they cannot
   reflow, only crush. ".gridtable" is the box they scroll inside. It replaces
   the inline "overflow:hidden" those wrappers used to carry for their rounded
   corners; overflow-x:auto clips the same corners and adds the scroll. */
.gridtable { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; max-width: 100%; }
.gridtable > * { min-width: 900px; }
/* The census grew a tenth column (GSC), and its fixed tracks now add up past
   900px — at which point the shared floor above stops being a floor and starts
   being a squeeze. Its own, stated once beside the template it belongs to. */
.gridtable.gridtable-census > * { min-width: 1180px; }

/* ────────────────────────────────────────────────────────────────────────────
   THE PAGE DRAWER, as an overlay (redesign #drawer, prototype ~:761).

   Server-rendered, still: the drawer IS the "?path=" render of the Pages route,
   and nothing here changes that. What this adds is the shape the design draws —
   a scrim over the dimmed census and a right-anchored panel — expressed in CSS
   over markup that already exists.

   The scrim is an ANCHOR, not a div: clicking it must close the drawer, and
   the close is a navigation to the same href the close link uses. That makes
   scrim-dismiss work with JavaScript off, and leaves the inline script owning
   only the one gesture a link cannot express (Escape).

   "position:fixed" on both means the panel can never widen the document, so
   there is no horizontal scroll leak at any width. Below 900px it takes the
   full width: a fixed-fraction panel on a narrow screen is a panel with its
   right half off-screen.

   IA-P1: the panel widened from a 520px sidebar to the redesign's ~75vw
   overlay (design/ia-simplification/README.md "Panel") — still the same
   fixed/scrim/dismiss mechanics, just a wider box. 75vw scales with the
   window; the cap keeps it from sprawling on an ultrawide, and 900px is
   where 75vw and "practically the whole screen" stop being different
   things, so that is also where the full-width breakpoint takes over. */
.drawerscrim {
  position: fixed; inset: 0; z-index: 8; background: rgba(7,10,9,.55);
  display: block; text-decoration: none !important;
}
.drawerpanel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 75vw; max-width: 1000px;
  min-width: 640px; z-index: 9;
  background: var(--panel, #121815); border-left: 1px solid var(--border-strong);
  padding: 22px 24px 32px; overflow-y: auto; overflow-x: hidden;
  animation: riseIn .18s ease-out; outline: none;
}
/* Nothing inside the panel may push it sideways: a long override value or a
   pretty-printed JSON-LD block wraps or scrolls in its own box instead. */
.drawerpanel .copyblock { max-width: 100%; overflow-x: auto; }
@media (max-width: 900px) { .drawerpanel { width: 100%; min-width: 0; border-left: 0; padding: 18px 16px 28px; } }
@media (prefers-reduced-motion: reduce) { .drawerpanel { animation: none; } }
/* The census underneath, dimmed rather than hidden — you opened one row of a
   list you are still standing in. */
.censusdim { opacity: .32; pointer-events: none; }

/* THE WHOLE CENSUS ROW IS THE DOOR (redesign #inventory).
   One link per row, stretched over the row by a pseudo-element, so the row is
   clickable everywhere without a single interactive element nested inside
   another. The action cell lifts itself above that overlay — a "Try again"
   button in the last column must stay its own control. */
.censusrow { position: relative; }
.censusrow:hover { background: rgba(255,255,255,.022); }
.censusrow.open { background: rgba(92,224,161,.06); }
.censusrow .rowopen::after { content: ''; position: absolute; inset: 0; }
.censusrow .rowact { position: relative; z-index: 1; }
