/* App surfaces (admin.html, client.html, status.html, map.html): the white
   SaaS console. Pure white canvas, white cards with a hairline and a
   barely-there shadow, a light rail with small line icons, ink as the only
   accent (no violet) on the primary button, the selected nav item and links.
   Red and amber mean state and are never the accent. Headings in the app are
   Instrument Sans (Lee's choice 2026-09-18, after Plex and Source Sans 3 were tried and rejected); Fraunces stays on the
   letter (read.html and client.html)
   only. (Lee's direction, 2026-09-10.)

   ---- The type scale (2026-09-18, readability pass; Lee: "the contrast is
   so bad and the text so small across the tool") -------------------------
   Five sizes and nothing else. Weight carries the hierarchy, not size. The
   whole scale moved up one step and the two greys moved two steps darker,
   because 83 of ~145 declarations were 14px, 31 were 12px, and most
   secondary text sat in a 4.6:1 grey; the IBM Plex swap (smaller x-height
   than Instrument Sans) made it read smaller still.

     13px  labels, meta, column headers, footnotes   (was 12)
     15px  secondary text: table cells, captions, controls   (was 14)
     16px  body, and anything a person reads in sentences   (was 15)
     17px  the lead sentence and section headings   (was 16)
     24px  display numbers and the page title

   Greys: --muted #374151 (10.3:1 on white), --faint #4B5563 (7.4:1). Dark
   mode: --muted #C4C9D2, --faint #9CA3AF. Nothing a person reads sits
   below 7:1.

   One label system: sentence case, normal tracking, everywhere. There is no
   tracked uppercase label anywhere in the app.

   ---- Score ----------------------------------------------------------------
   One component draws every score: GPScore.score(n, size) in scorecard.js,
   which owns one continuous colour rule (red below 34, amber 34 to 66, ink
   above 66) and the unit. The thresholds are published in the words of "How
   this score is made", so they are a rule rather than a house secret. Here
   there is only .sco: three sizes, the unit to the right of the number on its
   baseline at .6 of it, floored at 12px, and said once per surface — a list of
   scores passes an empty unit and lets its column header carry it. There is no
   green; a healthy score is simply not coloured. */

/* All tokens (colors, both themes, type scale, spacing, radius, shadow,
   fonts) moved to web/tokens.css, which every page that loads this file
   links first. Nothing below should introduce a new one-off value; add it
   to tokens.css instead. */

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { background: var(--canvas); }
body { margin: 0; color: var(--text); font-family: var(--sans); font-size: var(--fs-16); line-height: 1.55; -webkit-font-smoothing: antialiased; font-variant-numeric: tabular-nums; }
a { color: var(--text); text-decoration: underline; text-decoration-color: var(--rule); text-underline-offset: 2px; }
a:hover { text-decoration-color: var(--text); }
button { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
::placeholder { color: var(--faint); }
h1, h2, h3, h4, p { margin: 0; }

.app { display: flex; min-height: 100vh; }

/* ---- rail ---- */
.rail { width: var(--rail-w); flex: 0 0 auto; border-right: 1px solid var(--hairline); background: var(--rail); padding: 18px 12px 16px; position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; }
/* The brand block is its own thing and gets a clear 24px of air under it
   before the workspace switcher, so the two never read as one stack. */
.rail .brand { display: block; color: var(--text); text-decoration: none; font-size: var(--fs-15); font-weight: 600; padding: 2px 10px 2px; letter-spacing: -.01em; }
.rail .brand small { display: block; font-weight: 400; color: var(--muted); font-size: var(--fs-13); margin-top: 1px; }
.rail .workspace { display: flex; align-items: center; gap: var(--sp-6); margin: 24px 6px 14px; padding: 7px 8px; border: 1px solid var(--hairline); border-radius: var(--radius-ctl); background: var(--card); font-size: var(--fs-15); color: var(--muted); }
.rail .workspace b { color: var(--text); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail .workspace svg { margin-left: auto; flex: 0 0 auto; color: var(--faint); }
/* Sentence case: the one tracked uppercase label on a page is "The scorecard". */
.rail .section { font-size: var(--fs-13); font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--faint); padding: 10px 10px 6px; }
.rail nav { display: flex; flex-direction: column; gap: 1px; }
/* The company rows and the nav rows share one left edge: a 16px slot (an icon
   up top, the needs-a-hand dot down here) and then the name. The dot sits to
   the left of the name, exactly where it sits in the companies list. */
.rail .tenant-link { text-decoration: none; display: grid; grid-template-columns: 16px minmax(0, 1fr); align-items: center; gap: 9px; padding: 7px 10px; border-radius: var(--radius-ctl); cursor: pointer; border: 0; background: none; text-align: left; width: 100%; }
.rail .tenant-link:hover { background: var(--accent-tint); }
.rail .tenant-link.on { background: var(--accent-tint); }
.rail .tenant-link.on .name { color: var(--text); font-weight: 600; }
.rail .tenant-link .name { font-size: var(--fs-15); font-weight: 400; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail .tenant-link .meta { font-size: var(--fs-13); color: var(--muted); white-space: nowrap; }
/* One dot palette, three colours and nothing else: --dot-ok, --dot-warn,
   --dot-bad. The rail's flag is the failed red, the same token the alert and
   the sources table use. */
.rail .tenant-link .flag { display: block; width: 7px; height: 7px; border-radius: 50%; background: var(--dot-bad); justify-self: center; align-self: center; }
.rail .tenant-link .flag.none { background: none; }
.rail .tenant-link .flag.setup { background: none; border: 1.5px solid var(--faint); }
.rail .total { display: none; }

.rail .spacer { flex: 1; }
/* The icon sits in a 16px slot at the same left edge and the same vertical
   centre as the status dot on a company row, so the two line up down one x. */
.rail .rail-link { text-decoration: none; display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: var(--radius-ctl); font-size: var(--fs-15); color: var(--text); text-decoration: none; }
.rail .rail-link svg { flex: 0 0 16px; width: 16px; height: 16px; color: var(--faint); }
.rail .rail-link .meta { margin-left: auto; }
.rail .rail-link:hover { background: var(--accent-tint); }
/* Only the deepest item is ever marked: the root link drops its tint the
   moment a company is open, so two rows never read as selected at once. */
.rail .rail-link.on { background: var(--accent-tint); color: var(--text); font-weight: 600; }
.rail .rail-link.on svg { color: var(--text); }
.rail .rail-link .meta { color: var(--muted); font-size: var(--fs-13); }
/* How many companies need a hand: a badge, in the one red token. */
.rail .rail-link .count { margin-left: auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: var(--radius-pill); background: var(--bad-bg); color: var(--bad); font-size: var(--fs-13); font-weight: 600; display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums; }
.rail .rail-link .count:empty { display: none; }
.rail .empty { padding: 8px 10px; color: var(--muted); font-size: var(--fs-15); }

/* ---- main ---- */
main { flex: 1; min-width: 0; padding: 32px 40px 96px; }
.column { max-width: 980px; margin: 0 auto; }

/* ---- rail + main: phone width (S6) ---------------------------------------
   At 390px .app's flex row can't fit a 234px rail beside any usable main
   column, so below 700px the rail stops being a sticky side column and
   becomes a top strip: full width, no fixed height, no longer sticky. Its
   own contents (nav links, the company list, the add-a-client form) keep
   their normal vertical stacking, just narrower. Placed after the base
   main/.rail rules above so the padding override actually wins (same
   specificity, later wins). Nothing here touches the desktop layout above
   the breakpoint. */
@media (max-width: 700px) {
  .app { flex-direction: column; }
  .rail { width: 100%; height: auto; max-height: 60vh; overflow-y: auto; position: static; border-right: 0; border-bottom: 1px solid var(--hairline); }
  main { padding: 20px 16px 64px; }
}
.head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-24); margin-bottom: 14px; }
.head h1 { font-family: var(--sans); font-weight: 600; font-size: var(--fs-24); letter-spacing: -.02em; line-height: 1.2; }
/* One toolbar, two controls: a secondary outlined button out to the client
   page, and Rescan, the only primary control on the page. Both are buttons of
   the same height, so the toolbar has one shape instead of a link, an icon and
   a button jostling at three weights. */
.head .actions { display: flex; gap: 10px; align-items: center; margin-top: var(--sp-2); flex-wrap: nowrap; }
.head .actions .btn { height: 32px; font-size: var(--fs-15); padding: 0 14px; }
/* ---- company head: phone and tablet width (2026-09-27 responsive sweep) ---
   At 768px .head .actions (Report page, Published date, Rescan or Approve)
   is one row that does not wrap, so it runs past the page edge. Placed
   after the unconditional .head/.head .actions rules above (same
   specificity, later wins), or the nowrap above would keep beating it. The
   title and the actions row both wrap below 900px, the actions dropping
   under the title instead of pushing the page wider. */
@media (max-width: 900px) {
  .head { flex-wrap: wrap; }
  .head .actions { flex-wrap: wrap; }
  /* Every real control gets a 44px tap target at phone and tablet width
     without touching the desktop density above 900px: min-height leaves
     the declared height/padding alone and only raises the floor. */
  /* .conn-name is excluded: its whole row is the click target
     (.conn-list tr.rowlink, wired in connections.js), already well past
     44px via the row's own padding, so bumping the link itself would only
     force it out of its block layout for no gain. */
  .head .actions .btn, .btn, .chipc, .howlink, .back-link { min-height: 44px; display: inline-flex; align-items: center; }
  .companies td .co, .companies td .vac { min-height: 44px; display: inline-flex; align-items: center; }
  .catrow { min-height: 44px; }
  /* The side rail's own nav links are still the tall sidebar (that switch
     to a wrapped top strip happens below 700px), so between 700 and 900
     they need the same floor the wrapped version already gets at 911. */
  .rail .rail-link { min-height: 44px; }
  /* People's company link and any other plain <a> sitting alone in a
     table cell (no .co/.vac wrapper) is two pixels short of 44px at the
     row's normal 12px padding; nudging the whole table's row padding
     clears it without a one-off class. */
  table.tbl td { padding: 13px 10px; }
  /* The read tab's own fold controls (Details, Not checked) are real
     tap targets, same as web/read.html's .fold summary, just narrower.
     .formula and .rowdet summaries are left alone: they sit in the
     dense audit/job log and the "we publish the formula" footnote,
     both power-user reading contexts rather than something tapped in
     the normal flow. */
  .card details > summary, details.notchecked summary { min-height: 44px; }
}

/* Where you are: underline tabs under the title, in the same hairline
   vocabulary as every other rule on the page. The rule under the tabs is the
   tab row's own, so the subhead does not draw a second one. */
.subhead { margin-bottom: 0; }
.subhead .tabs { margin-bottom: 0; }
.head .sub { color: var(--muted); font-size: var(--fs-15); margin-top: var(--sp-4); max-width: 64ch; }
/* The caption under a page title: where a standing figure lives when it is
   context for the page rather than a row in the table below it. */
.head .cap { color: var(--faint); font-size: var(--fs-15); margin-top: var(--sp-4); max-width: 78ch; }
.head .sub a { color: var(--muted); text-decoration: none; border-bottom: 1px solid var(--rule); }
.head .sub a:hover { color: var(--accent); }
.head .sub a:hover { color: var(--text); }
.chip { display: inline-flex; align-items: center; gap: var(--sp-8); height: 30px; padding: 0 12px; border-radius: var(--radius-pill); border: 1px solid var(--hairline); background: var(--card); font-size: var(--fs-15); color: var(--muted); white-space: nowrap; margin-top: var(--sp-4); }
.chip .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dot-ok); }
.chip.bad { color: var(--bad); border-color: var(--bad-border); background: var(--bad-bg); } .chip.bad .dot { background: var(--dot-bad); }
.chip.warn { color: var(--warn); border-color: var(--warn-border); background: var(--warn-bg); } .chip.warn .dot { background: var(--dot-warn); }

/* ---- tabs ---- */
.tabs { display: flex; gap: var(--sp-2); border-bottom: 1px solid var(--hairline); margin-bottom: var(--sp-24); }
.tab { display: inline-flex; align-items: center; gap: 7px; padding: 10px 14px; font-size: var(--fs-15); font-weight: 500; color: var(--muted); background: none; border: 0; border-bottom: 2px solid transparent; cursor: pointer; margin-bottom: -1px; }
.tab:hover { color: var(--text); }
.tab.on { color: var(--text); border-bottom-color: var(--accent); font-weight: 500; }
.tab .badge { min-width: 18px; height: 18px; padding: 0 6px; border-radius: var(--radius-pill); font-size: var(--fs-13); font-weight: 600; display: inline-flex; align-items: center; justify-content: center; background: var(--bad-bg); color: var(--bad); }
.tab .badge.warn { background: var(--warn-bg); color: var(--warn); }
.tab .badge.neutral { background: var(--accent-tint); color: var(--muted); }
.panel { display: none; } .panel.on { display: block; padding-top: var(--sp-24); }

/* ---- attention list ---- */
.attention { margin-bottom: 20px; border: 1px solid var(--hairline); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; }
.attention .item { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 14px; align-items: center; padding: 12px 14px 12px 16px; border-bottom: 1px solid var(--hairline); min-height: 56px; box-shadow: inset 3px 0 0 var(--bad); }
.attention .item.warn { box-shadow: inset 3px 0 0 var(--warn); }
.attention .item.neutral { box-shadow: inset 3px 0 0 var(--rule); }
.attention .item.child { padding-left: 36px; box-shadow: none; min-height: 44px; }
.attention .item.child .what { color: var(--muted); }
.attention .item.compact { min-height: 40px; padding-top: var(--sp-8); padding-bottom: var(--sp-8); }
.attention .item:last-child { border-bottom: 0; }
.attention .bar { display: none; }
.attention .item.compact { min-height: 44px; }
.attention .what { font-size: var(--fs-15); }
.attention .what small { display: block; color: var(--muted); font-size: var(--fs-15); margin-top: 1px; }
.attention .allclear { padding: 14px 16px; color: var(--muted); font-size: var(--fs-15); display: flex; align-items: center; gap: 10px; }
.attention .allclear .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dot-ok); }

/* ---- key line: the counts as one sentence, judgment in colour ---- */
.kline { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 22px; padding: 4px 0 18px; font-size: var(--fs-15); color: var(--muted); }
.kline .k { display: inline-flex; align-items: baseline; gap: var(--sp-6); }
.kline .k b { font-size: var(--fs-24); font-weight: 600; color: var(--text); letter-spacing: -.01em; }
.kline .k.bad b { color: var(--bad); } .kline .k.warn b { color: var(--warn); } .kline .k.zero b { color: var(--faint); }
.kline .sep { color: var(--rule); }

/* ---- stat strip (legacy, unused after iteration 3) ---- */
.strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--hairline); border: 1px solid var(--hairline); border-radius: var(--radius); overflow: hidden; margin-bottom: 20px; }
.strip .stat { background: var(--card); padding: 14px 16px; min-height: 84px; }
.strip .stat .n { font-size: var(--fs-24); font-weight: 600; letter-spacing: -.01em; line-height: 1.1; }
.strip .stat .n.bad { color: var(--bad); } .strip .stat .n.warn { color: var(--warn); } .strip .stat .n.zero { color: var(--faint); }
.strip .stat .l { font-size: var(--fs-15); color: var(--muted); margin-top: var(--sp-6); }
.strip .stat .l b { color: var(--text); font-weight: 500; }
.strip.five { grid-template-columns: repeat(5, 1fr); }
.strip.three { grid-template-columns: repeat(3, 1fr); }
.asof { font-size: var(--fs-13); color: var(--faint); margin: -12px 0 20px; }

/* ---- cards ---- */
.card { background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius); box-shadow: var(--shadow); padding: var(--sp-16); margin-bottom: var(--sp-16); }
.card > h2 { font-size: var(--fs-16); font-weight: 600; letter-spacing: -.005em; margin-bottom: var(--sp-12); }
.card > h2 .n { color: var(--muted); font-weight: 400; margin-left: var(--sp-8); font-size: var(--fs-15); }
.card .cardhead { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-16); margin-bottom: var(--sp-12); }
.card .cardhead h2 { margin: 0; }
.card .cardhead .hint { color: var(--muted); font-size: var(--fs-15); }
.card .under { color: var(--muted); font-size: var(--fs-15); margin: -6px 0 14px; }
.card p.lead { color: var(--muted); font-size: var(--fs-15); margin-bottom: var(--sp-12); max-width: 70ch; }
.grid2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-16); align-items: start; }
.grid-main-side { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--sp-16); align-items: start; }
@media (max-width: 1100px) { .grid2, .grid-main-side { grid-template-columns: 1fr; } }
.group-title { font-size: var(--fs-13); font-weight: 600; color: var(--faint); letter-spacing: 0; text-transform: none; margin: 22px 0 8px; }
.group-title:first-child { margin-top: 0; }

/* ---- tables ---- */
table.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-15); }
table.tbl th { text-align: left; font-size: var(--fs-13); color: var(--faint); font-weight: 600; padding: 8px 10px; border-bottom: 1px solid var(--rule); white-space: nowrap; background: var(--card); }
/* No height on a cell. A cell shorter than its row used to paint its own
   border-bottom part-way up, which is the stray underline that appeared
   beneath every score in the companies list. Padding sets the rhythm now. */
table.tbl td { padding: 12px 10px; border-bottom: 1px solid var(--hairline); vertical-align: middle; }
/* The header stays put while a long table scrolls past it. Needs a table that
   is not inside .tbl-wrap, since that wrapper is its own scroll box. */
table.tbl.stick th { position: sticky; top: 0; z-index: 2; box-shadow: inset 0 -1px 0 var(--rule); }
table.tbl tr:last-child td { border-bottom: 0; }
table.tbl tr:hover td { background: var(--rail); }
/* Figures line up in a column: tnum is asked for explicitly, because the body
   rule alone does not survive every face's default figure set. */
table.tbl td.num, table.tbl th.num { text-align: right; font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1, "lnum" 1; }
table.tbl td.id { font-family: var(--mono); font-size: var(--fs-15); }
table.tbl td.dim { color: var(--muted); }
table.tbl td.nw { white-space: nowrap; }
table.tbl tr.is-bad td:first-child { box-shadow: inset 3px 0 0 var(--bad); }
table.tbl tr.is-warn td:first-child { box-shadow: inset 3px 0 0 var(--warn); }
table.tbl td .note { color: var(--muted); font-size: var(--fs-15); display: block; margin-top: var(--sp-2); max-width: 60ch; white-space: normal; }
.tbl-wrap { overflow-x: auto; }
.rowactions { display: flex; gap: var(--sp-4); justify-content: flex-end; }

/* ---- status: one chip everywhere (a dot, then a word) ----
   Fixed width wherever chips stack, so the dots line up in a column and the
   eye reads down them instead of hunting. */
.pill { display: inline-flex; align-items: center; gap: var(--sp-6); font-size: var(--fs-15); white-space: nowrap; }
.tbl .pill, .cardhead .pill { min-width: 148px; }
.pill .dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--dot-none); }
.pill.neutral .dot { background: var(--dot-none); }
.pill.good .dot { background: var(--dot-ok); }
.pill.warn .dot { background: var(--dot-warn); } .pill.warn { color: var(--warn); }
.pill.bad .dot { background: var(--dot-bad); } .pill.bad { color: var(--bad); }
.pill.neutral { color: var(--muted); }

/* ---- buttons: primary, secondary, destructive (text), link ---- */
.btn { height: 34px; padding: 0 14px; border-radius: var(--radius-ctl); font-size: var(--fs-15); font-weight: 500; cursor: pointer; border: 1px solid transparent; background: none; white-space: nowrap; display: inline-flex; align-items: center; gap: var(--sp-6); text-decoration: none; }
.btn.primary { background: var(--accent); color: var(--on-accent); }
.btn.primary:hover { background: var(--accent-hover); }
.btn.secondary { border-color: var(--rule); color: var(--text); background: var(--card); }
.btn.secondary:hover { border-color: var(--faint); }
.btn.destructive { color: var(--bad); border-color: var(--bad-border); }
.btn.primary.danger { background: var(--bad); }
.btn.destructive:hover { background: var(--bad-bg); }
.btn.link { color: var(--muted); padding: 0 6px; height: 28px; }
.btn.link:hover { color: var(--text); background: var(--accent-tint); }
.btn.sm { height: 28px; padding: 0 10px; font-size: var(--fs-15); }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btnrow { display: flex; align-items: center; gap: var(--sp-8); margin-top: 14px; }

/* ---- forms ---- */
label { display: block; font-size: var(--fs-15); color: var(--muted); margin-bottom: 5px; }
input[type="text"], input[type="number"], textarea, select { width: 100%; background: var(--card); border: 1px solid var(--rule); border-radius: var(--radius-ctl); padding: 7px 10px; font: inherit; color: var(--text); font-size: var(--fs-15); }
textarea { font-family: var(--mono); font-size: var(--fs-15); min-height: 120px; resize: vertical; line-height: 1.5; }
textarea.tall { min-height: 200px; }
textarea.csv { min-height: 140px; }
input:focus, textarea:focus, select:focus { border-color: var(--faint); }
.field { margin-bottom: var(--sp-12); }
.field-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--sp-12); }
.error-text { color: var(--bad); font-size: var(--fs-15); margin-top: var(--sp-8); white-space: pre-wrap; }
.help { color: var(--muted); font-size: var(--fs-15); margin-top: var(--sp-6); line-height: 1.5; }
.phrase { font-size: var(--fs-15); }
.phrase code { font-family: var(--mono); font-size: var(--fs-15); background: var(--accent-tint); padding: 1px 5px; border-radius: var(--radius-sm); }

/* ---- list rows ---- */
.rows { display: flex; flex-direction: column; }
.rows .r { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-12); padding: 9px 0; border-bottom: 1px solid var(--hairline); font-size: var(--fs-15); }
.rows .r:last-child { border-bottom: 0; }
.rows .r .k { color: var(--muted); }

/* ---- score ---- */
.score { display: flex; align-items: baseline; gap: 10px; }
.score .big { font-size: var(--fs-24); font-weight: 600; letter-spacing: -.01em; }
.score .of { color: var(--faint); font-size: var(--fs-15); }
.meter { height: 6px; border-radius: 3px; background: var(--accent-tint); margin: 8px 0 6px; overflow: hidden; }
.meter i { display: block; height: 100%; background: var(--good); }
.meter i.warn { background: var(--warn); } .meter i.bad { background: var(--bad); }

/* ---- reports ---- */
.report-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-16); align-items: center; padding: 14px 16px; border: 1px solid var(--hairline); border-radius: var(--radius); box-shadow: var(--shadow); background: var(--card); margin-bottom: 10px; }
.report-card .t { font-size: var(--fs-15); font-weight: 500; }
.report-card .m { color: var(--muted); font-size: var(--fs-15); margin-top: var(--sp-2); display: flex; gap: 10px; align-items: center; }
.reading { margin-top: var(--sp-16); }
.reading .verdict { font-size: var(--fs-16); line-height: 1.6; max-width: 70ch; margin-bottom: var(--sp-16); }
.reading textarea.verdict-edit { font-family: var(--sans); font-size: var(--fs-16); line-height: 1.6; min-height: 140px; }
.reading .decide { display: flex; align-items: center; gap: var(--sp-8); padding-top: 14px; margin-top: var(--sp-16); border-top: 1px solid var(--hairline); }
.reading .decide .fill { flex: 1; }
.callout { border-left: 3px solid var(--ink); padding: 4px 14px; font-size: var(--fs-15); margin: 14px 0; }
.callout.bad { border-left-color: var(--bad); }

/* ---- checklist (empty tenant) ---- */
.checklist { display: flex; flex-direction: column; }
.checklist .step { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; gap: 14px; align-items: center; padding: 14px 0; border-bottom: 1px solid var(--hairline); }
.checklist .step:last-child { border-bottom: 0; }
.checklist .num { width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--rule); display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-13); color: var(--muted); }
.checklist .num.done { background: var(--good); border-color: var(--good); color: var(--on-accent); }
.checklist .step .t { font-size: var(--fs-15); font-weight: 500; }
.checklist .step .d { color: var(--muted); font-size: var(--fs-15); margin-top: var(--sp-2); }
.checklist .step.later { opacity: .55; }
.checklist .step.current .num { border-color: var(--accent); color: var(--text); font-weight: 600; }

/* ---- misc ---- */
.muted { color: var(--muted); } .faint { color: var(--faint); } .mono { font-family: var(--mono); }
.empty { color: var(--muted); font-size: var(--fs-15); padding: 18px 0; }
.empty b { color: var(--text); font-weight: 500; }
.toast { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); background: var(--ink); color: var(--canvas); padding: 10px 16px; border-radius: var(--radius-pill); font-size: var(--fs-15); opacity: 0; transition: opacity .15s; pointer-events: none; }
.toast.on { opacity: 1; }
.toast.err { background: var(--bad); }
.loading { color: var(--faint); font-size: var(--fs-15); padding: 24px 0; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.row { display: flex; align-items: center; gap: var(--sp-8); }

/* ---- file drop zone ---- */
.drop { border: 1px dashed var(--rule); border-radius: var(--radius); padding: 18px; text-align: center; color: var(--muted); font-size: var(--fs-15); cursor: pointer; background: var(--canvas); }
.drop:hover, .drop.over { border-color: var(--faint); background: var(--accent-tint); }
.drop b { color: var(--text); font-weight: 500; }
.drop input { display: none; }
.drop .picked { color: var(--text); }
/* ---- settings rows ---- */
.setting { display: grid; grid-template-columns: 150px minmax(0, 1fr) auto; gap: 14px; align-items: center; padding: 10px 0; border-bottom: 1px solid var(--hairline); font-size: var(--fs-15); }
.setting:last-child { border-bottom: 0; }
.setting .k { color: var(--muted); }
.setting .v { font-family: var(--mono); font-size: var(--fs-15); overflow-wrap: anywhere; cursor: copy; line-height: 1.4; }
.setting.flagged { box-shadow: inset 3px 0 0 var(--bad); padding-left: var(--sp-12); }
.setting.flagged .k { color: var(--bad); }
.setting .v.none { font-family: var(--sans); color: var(--faint); }
.setting input { max-width: 360px; }

/* ---- offer ladder row editor (the .setting row that holds #pf-offer-ladder-tbl) ---- */
.setting.ladder-row .v { font-family: var(--sans); cursor: default; }
.setting.ladder-row table.tbl { margin-bottom: 10px; }
.setting.ladder-row table.tbl input, .setting.ladder-row table.tbl select { max-width: none; width: 100%; font-size: var(--fs-15); }
.setting.ladder-row table.tbl td:first-child { width: auto; }
.setting.ladder-row table.tbl td:nth-child(2) { width: 110px; }
.setting.ladder-row table.tbl td:nth-child(3) { width: 140px; }
.setting.ladder-row table.tbl td:last-child { width: 1%; white-space: nowrap; }

/* ---- profile field rows (web/profile.js's card/fieldRow; PLAN.md E21
   readability pass, 2026-09-18 — Lee: "the tool is hard to read"). A
   profile field's value is prose (what it sells, who pays, the offer
   ladder, evidence sentences), never a short copyable id, so it overrides
   .setting .v's monospace/copy-cursor back to the sans at body size — the
   same override pattern .setting.ladder-row .v above already uses, kept
   its own block because a profile row stacks three lines (the humanised
   value, its evidence, its source) instead of holding one value. */
.setting.profile-field { align-items: start; }
.setting.profile-field .v { font-family: var(--sans); font-size: var(--fs-16); line-height: 1.55; color: var(--text); cursor: auto; overflow-wrap: anywhere; display: flex; flex-direction: column; gap: var(--sp-4); }
.profile-field .pf-lead { font-weight: 500; }
.profile-field .pf-ladder { display: flex; flex-direction: column; gap: var(--sp-2); }
.profile-field .pf-evidence { font-weight: 400; color: var(--muted); font-size: var(--fs-15); line-height: 1.5; }
.profile-field .pf-source { font-size: var(--fs-13); color: var(--faint); }
.profile-field .pf-source a { color: inherit; }
.profile-field .pill.sm { font-size: var(--fs-13); padding: 1px 8px; border-radius: var(--radius-pill); background: var(--accent-tint); margin-left: var(--sp-2); }
.profile-field .pill.warn.sm { background: var(--warn-bg); }

/* ---- all-clear line ---- */
.clear { display: flex; align-items: center; gap: var(--sp-12); padding: 16px 18px; background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius); box-shadow: var(--shadow); font-size: var(--fs-15); margin-bottom: 20px; }
.clear .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dot-ok); }
.clear .rest { color: var(--muted); }
/* ---- reorder arrows ---- */
.arrows { display: inline-flex; gap: var(--sp-2); }
.arrows button { width: 24px; height: 24px; border: 1px solid var(--hairline); background: var(--card); border-radius: var(--radius-ctl); cursor: pointer; color: var(--muted); font-size: var(--fs-13); line-height: 1; }
.arrows button:hover { color: var(--text); border-color: var(--rule); }
.arrows button:disabled { opacity: .3; cursor: default; }
/* ---- steps (instructions) ---- */
ol.steps { margin: 8px 0 0; padding-left: 20px; color: var(--muted); font-size: var(--fs-15); }
ol.steps li { margin-bottom: var(--sp-4); }

/* ---- the root view ---- */
.who { font-size: var(--fs-15); color: var(--muted); margin-bottom: var(--sp-2); }
.trend { color: var(--muted); font-size: var(--fs-15); }
.trend.up { color: var(--good); } .trend.down { color: var(--bad); }
.chips { display: flex; gap: var(--sp-8); flex-wrap: wrap; }
.chipc { font-size: var(--fs-15); color: var(--muted); border: 1px solid var(--hairline); border-radius: var(--radius-pill); padding: 3px 10px; background: none; cursor: pointer; font-family: inherit; }
.chipc.on { border-color: var(--accent); background: var(--accent); color: var(--on-accent); }
.chipc:disabled { cursor: default; }
.chipc b { color: var(--text); font-weight: 500; }

/* ---- headline numbers (report figures on overview and client) ---- */
.figures { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); margin: 4px 0 20px; }
.figures .f { padding: 14px 16px 14px 0; }
.figures .f + .f { padding-left: var(--sp-16); border-left: 1px solid var(--hairline); }
.figures .f .n { font-size: var(--fs-24); font-weight: 600; letter-spacing: -.01em; line-height: 1.1; }
.figures .f.lead .n { font-size: var(--fs-24); }
.figures .f.bad .n { color: var(--bad); }
.figures .f .l { font-size: var(--fs-15); color: var(--muted); margin-top: var(--sp-4); }
.figures .f .d { font-size: var(--fs-15); margin-top: var(--sp-2); }
.figures .f .d.up { color: var(--good); } .figures .f .d.down { color: var(--muted); }
.sticky { position: sticky; top: 0; background: var(--canvas); z-index: 2; }

/* ---- pull quote (next move) ---- */
.pull { font-size: var(--fs-17); line-height: 1.45; letter-spacing: -.005em; padding: 14px 0 14px 18px; border-left: 3px solid var(--ink); margin: 20px 0; max-width: 64ch; }
.pull small { display: block; font-family: var(--sans); font-size: var(--fs-15); color: var(--muted); margin-top: var(--sp-6); letter-spacing: 0; }
/* ---- skeleton ---- */
.skel { height: 12px; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--accent-tint), var(--hairline), var(--accent-tint)); background-size: 200% 100%; animation: sk 1.2s linear infinite; margin: 10px 0; }
@keyframes sk { from { background-position: 200% 0; } to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .skel { animation: none; } }
/* ---- callout with a title, a sentence, and a button ---- */
.fix { border-left: 3px solid var(--bad); padding: 6px 0 6px 16px; margin: 12px 0 16px; }
.fix .t { font-weight: 600; font-size: var(--fs-15); }
.fix .s { color: var(--muted); font-size: var(--fs-15); margin: 2px 0 10px; max-width: 70ch; }
.fix details summary { cursor: pointer; color: var(--muted); font-size: var(--fs-15); margin-top: var(--sp-8); }
/* ---- chip groups ---- */
.chipgroup { display: flex; align-items: center; gap: var(--sp-6); margin-right: 18px; flex-wrap: wrap; }
.chipgroup .lbl { font-size: var(--fs-13); color: var(--faint); margin-right: var(--sp-2); }
/* ---- id with copy ---- */
.idc { font-family: var(--mono); font-size: var(--fs-13); color: var(--muted); cursor: copy; }
/* ---- rail: add a client ---- */
.rail form { padding: 8px 10px; }
.rail form input { font-size: var(--fs-15); padding: 6px 8px; }
.rail form input::placeholder { font-size: var(--fs-13); }
/* ---- the site tag's install snippet (PLAN.md E9) ---- */
.card details > summary { cursor: pointer; color: var(--muted); font-size: var(--fs-15); margin-top: 10px; }
.snippet { font-family: var(--mono); font-size: var(--fs-13); line-height: 1.55; background: var(--accent-tint); border: 1px solid var(--hairline); border-radius: var(--radius-ctl); padding: 10px 12px; margin: 10px 0 6px; white-space: pre-wrap; overflow-wrap: anywhere; }

/* ---- the header's decision, U3 goal 2 --------------------------------------
   The confirm step replaces the primary button in place: no browser
   confirm() dialog, just the question and Approve/Cancel where the button
   was. */
.confirm-inline { display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.confirm-inline .q { font-size: var(--fs-15); color: var(--muted); max-width: 42ch; }
#tenant-actions-decision { display: contents; }
.head .actions .muted { font-size: var(--fs-15); white-space: nowrap; }
#panel-read .g-state { margin-bottom: 20px; }
#panel-read .g-lead { margin-bottom: 28px; }

/* ---- the read's state line, U3 goal 3 --------------------------------------
   One line, top of the tab: what state the read is in, and how much of the
   check set is answered. Everything the old header's KPI stack led with. */
.g-state { font-size: var(--fs-15); color: var(--muted); display: flex; align-items: center; gap: var(--sp-8); margin-bottom: var(--sp-4); }
.g-state .st { font-weight: 600; color: var(--text); }
.g-state .sep { color: var(--faint); }

/* ---- the read as a scorecard (PLAN.md E13) ---------------------------------
   One grid for the whole company page, five parts to seven, and every block on
   it starts on one of its two columns. Row one is the two tiles beside what
   matters most; row two the scorecard's own header across both; row three the
   ranked list beside the checks; row four the moves across the foot. Nothing
   is left with a blank right third. */
.pagegrid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: var(--sp-32); row-gap: 26px; align-items: start; }
.g-tiles { grid-column: 1; grid-row: 1; align-self: start; }
.g-lead { grid-column: 2; grid-row: 1; }
.g-scorehead { grid-column: 1 / -1; grid-row: 2; }
.g-list { grid-column: 1; grid-row: 3; }
.g-pane { grid-column: 2; grid-row: 3; }
/* The moves carry the same section marker as the scorecard: one label system,
   one size, one case, a rule underneath. */
.g-moves { grid-column: 1 / -1; grid-row: 4; }
.g-moves .scorehead { margin-bottom: 18px; }
@media (max-width: 900px) {
  .pagegrid { grid-template-columns: minmax(0, 1fr); row-gap: 20px; }
  .g-tiles, .g-lead, .g-scorehead, .g-list, .g-pane, .g-moves { grid-column: 1; grid-row: auto; }
  .g-lead { order: -1; }
}
/* What matters most: the sentence the whole page exists to deliver, set in the
   right column's own measure. Regular weight with the first sentence bold, so
   the paragraph stops competing with the alert and the Overall tile for the
   eye and instead hands it one line to read first. (Size stays 16: Lee's
   floor is that nothing gets smaller than it is today.) */
.onething { margin: 0; }
.onething p { font-size: var(--fs-17); font-weight: 400; line-height: 1.55; letter-spacing: -.005em; max-width: 68ch; }
.onething p b { font-weight: 600; }
.onething small { display: block; font-size: var(--fs-13); font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--faint); margin-bottom: var(--sp-8); }
.sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ---- the score, one component, three sizes --------------------------------
   Number then unit, always in that order and always on one baseline. The unit
   is .6 of the number, and 12px is the floor because nothing in this app is
   smaller than 12px. Colour comes from GPScore.scoreTone and nowhere else. */
.sco { display: inline-flex; align-items: baseline; gap: var(--sp-6); font-variant-numeric: tabular-nums; color: var(--text); white-space: nowrap; }
.sco b { font-weight: 600; letter-spacing: -.02em; line-height: 1; }
.sco small { color: var(--faint); font-weight: 400; letter-spacing: 0; }
.sco-hero b { font-size: var(--fs-24); } .sco-hero small { font-size: var(--fs-15); }
.sco-row b { font-size: var(--fs-24); } .sco-row small { font-size: var(--fs-13); }
.sco-inline b { font-size: var(--fs-17); } .sco-inline small { font-size: var(--fs-13); }
.sco.warn b { color: var(--warn); } .sco.bad b { color: var(--bad); }
.sco.nil b { color: var(--rule); font-weight: 400; }

/* The alert: the only red line on the page, and it is meant to be absent. It
   stays inside the content column — the old band bled 14px past it on both
   sides — and it says the plain error, because the page's own subtitle is
   where the phrase "needs a hand" lives, once. Its dot is the failed red from
   the one dot palette. */
.alert { display: flex; align-items: baseline; gap: 10px; padding: 10px 14px; border: 1px solid var(--bad-border); background: var(--bad-bg); border-radius: var(--radius); font-size: var(--fs-15); color: var(--bad); margin: 0 0 26px; }
.alert .dot { display: block; width: 7px; height: 7px; border-radius: 50%; background: var(--dot-bad); flex: 0 0 auto; position: relative; top: -1px; }
.alert .w { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.alert a { color: var(--bad); margin-left: auto; white-space: nowrap; font-weight: 600; }

/* The scorecard's own header: its name on the left, and on the right the date
   it was scored — which is also the control for reading the two first-party
   categories from an earlier week. Sentence case, like every other label. */
.scorehead { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-16); padding-bottom: 10px; border-bottom: 1px solid var(--hairline); }
.scorehead .t { font-size: var(--fs-13); font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--faint); }
.scorehead .scored { font-size: var(--fs-13); color: var(--faint); }

/* The category tiles: a titled header band (Lee's reference dashboard), the
   score below it. The letter and the client page lay them out as a grid; the
   admin uses exactly two of them, Overall and Checked, above its list. */
.cstrip { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 10px; margin: 0 0 12px; }
/* The admin's two: they hold the top of the left column, on the same line the
   paragraph beside them starts on. Overall is the wider of the pair because
   its header band also carries the link to how the score is made — beside the
   label it explains, not orphaned under the tiles. The band stays (Lee's
   reference dashboard) and is simply tighter. */
.cstrip.duo { grid-template-columns: 1.4fr 1fr; gap: var(--sp-12); margin: 0; }
.cstrip.duo .ctile .h { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 7px 12px; }
.cstrip.duo .ctile .b { padding: 12px 12px 14px; }
.howlink { font-size: var(--fs-13); font-weight: 400; color: var(--muted); background: none; border: 0; border-bottom: 1px solid var(--rule); padding: 0; cursor: pointer; white-space: nowrap; font-family: inherit; line-height: 1.2; }
.howlink:hover { color: var(--text); border-bottom-color: var(--text); }
.howtext { color: var(--muted); font-size: var(--fs-15); line-height: 1.6; margin: 12px 0 0; max-width: 76ch; }
.ctile { text-decoration: none; background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius); box-shadow: var(--shadow); display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
a.ctile:hover { border-color: var(--rule); }
a.ctile:hover .h { background: var(--accent-tint); }
.ctile .h { display: block; padding: 9px 12px; background: var(--rail); border-bottom: 1px solid var(--hairline); font-size: var(--fs-13); font-weight: 600; color: var(--muted); line-height: 1.35; }
.cstrip:not(.duo) .ctile .h { font-weight: 500; min-height: 38px; }
.ctile .b { display: block; padding: 12px 12px 14px; }
.ctile .n { display: block; font-size: var(--fs-24); font-weight: 600; letter-spacing: -.02em; line-height: 1; color: var(--text); }
.ctile.warn .n { color: var(--warn); } .ctile.bad .n { color: var(--bad); }
.ctile .m { display: block; font-size: var(--fs-13); color: var(--muted); margin-top: 7px; }
/* A number we do not have is quieter and smaller than one we do. */
.nil { font-size: var(--fs-17); font-weight: 400; color: var(--rule); letter-spacing: 0; }
td.score .nil { font-size: var(--fs-15); }

/* ---- the scorecard: a ranked list, and the pane it drives ------------------
   Neither side is a card. Both are bare panels on the white canvas, separated
   by one hairline rule down the gutter, and their headers share a top edge and
   a baseline so the two columns read as one table with a spine.

   The numeral is the truth; the bar is only a comparison device, so it is a
   quiet 4px track along the full width of the row's bottom edge rather than a
   120px widget arguing with the number beside it. One ink fill at one opacity,
   red only on a row that is actually failing. How many of a row's checks were
   checked is said once, in the Checked tile, and not again per row. */
.listhead, .panehead { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-16); min-height: 40px; padding-bottom: 10px; border-bottom: 1px solid var(--hairline); }
.panehead h2 { font-family: var(--sans); font-size: var(--fs-17); font-weight: 600; letter-spacing: -.01em; }
.listhead .t { font-size: var(--fs-13); font-weight: 600; color: var(--faint); line-height: 1.4; }

.catlist { border: 0; background: none; box-shadow: none; }
.catrow { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; column-gap: var(--sp-12); width: 100%; padding: 13px 12px 16px; border: 0; background: none; text-align: left; cursor: pointer; font: inherit; color: var(--text); }
.catlist .catrow + .catrow { border-top: 1px solid var(--hairline); }
.catrow:hover { background: var(--rail); }
/* Selected: a tint and a 2px ink rule down the left edge. */
.catrow.on { background: var(--accent-tint); box-shadow: inset 2px 0 0 var(--ink); }
.catrow.on .nm { font-weight: 600; }
.catrow:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.catrow .nm { grid-column: 1; font-size: var(--fs-15); line-height: 1.3; min-width: 0; }
.catrow .sco { grid-column: 2; justify-self: end; }
.catrow .track { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; background: var(--accent-tint); }
.catrow.on .track { background: var(--card); }
.catrow .track i { display: block; height: 100%; background: var(--ink); opacity: .5; }
.catrow .track i.bad { background: var(--bad); opacity: 1; }
.catrow.unscored .nm { color: var(--muted); }
.catrow .nsy { grid-column: 2; justify-self: end; font-size: var(--fs-13); color: var(--faint); }

/* The pane: the hairline down the gutter is the only thing dividing the two
   columns, and it is drawn on the pane so row one above it stays untouched. */
.g-pane { position: relative; }
.g-pane::before { content: ""; position: absolute; left: -16px; top: 0; bottom: 0; width: 1px; background: var(--hairline); }

/* The Sources list the pane's reference numerals point at. Without it the
   numerals are decoration; with it they are a claim a reader can check. */
.sources { margin-top: var(--sp-24); padding-top: var(--sp-12); border-top: 1px solid var(--hairline); }
.sources .t { display: block; font-size: var(--fs-13); font-weight: 600; color: var(--faint); margin-bottom: var(--sp-8); }
.sources .s { display: grid; grid-template-columns: 18px minmax(0, 1fr); gap: var(--sp-8); font-size: var(--fs-15); color: var(--muted); line-height: 1.5; padding: 3px 0; overflow-wrap: anywhere; }
.sources .n { color: var(--faint); font-variant-numeric: tabular-nums; }
.sources a { color: var(--text); text-decoration-color: var(--rule); overflow-wrap: anywhere; }
.grp { margin-top: 18px; }
.gl { display: inline-flex; align-items: center; gap: var(--sp-6); font-size: var(--fs-13); font-weight: 600; color: var(--muted); }
.gl .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--dot-none); flex: 0 0 auto; }
.gl.good .dot { background: var(--dot-ok); } .gl.warn .dot { background: var(--dot-warn); } .gl.bad .dot { background: var(--dot-bad); }
details.notchecked summary { cursor: pointer; color: var(--muted); }
.checks.plain { border-top: 0; margin-top: var(--sp-4); }
.chkp { padding: 9px 0; border-bottom: 1px solid var(--hairline); }
.chkp:last-child { border-bottom: 0; }
.chkp .l { display: block; font-size: var(--fs-16); font-weight: 600; line-height: 1.4; }
.chkp .e { display: block; font-size: var(--fs-16); color: var(--muted); margin-top: var(--sp-2); line-height: 1.55; max-width: 72ch; }
/* The fix is a plain muted second line behind one small wrench. Four bold
   "Fix:" labels stacked down a pane read as something a machine stamped on. */
.chkp .f { display: grid; grid-template-columns: 14px minmax(0, 1fr); gap: 7px; align-items: start; font-size: var(--fs-16); color: var(--muted); margin-top: 5px; line-height: 1.55; max-width: 72ch; }
.chkp .f .wr { color: var(--faint); margin-top: 5px; }
.chkp.neutral .l { color: var(--muted); font-weight: 400; }

/* Narrow: the list stacks above the pane and its rows become tabs. */
@media (max-width: 900px) {
  #tenant-site { display: inline-flex; align-items: center; min-height: 44px; }
  .viewas-banner.on { padding-top: 0; padding-bottom: 0; }
  .g-pane::before { display: none; }
  .listhead { display: none; }
  .catlist { display: flex; flex-wrap: wrap; gap: var(--sp-6); border: 0; background: none; box-shadow: none; overflow: visible; }
  .catlist .catrow + .catrow { border-top: 0; }
  .catrow { width: auto; grid-template-columns: auto auto; gap: var(--sp-8); padding: 7px 11px; border: 1px solid var(--hairline); border-radius: var(--radius-ctl); background: var(--card); }
  .catrow .track, .catrow .nsy { display: none; }
  .catrow .sco b { font-size: var(--fs-15); }
  .catrow.on { box-shadow: inset 0 -2px 0 var(--ink); }
}

/* ---- disclosures ----------------------------------------------------------
   No native triangle anywhere: it is a different shape in every browser and it
   is never the size we want. One 12px chevron, rotated when the thing opens. */
details > summary { list-style: none; display: flex; align-items: center; gap: var(--sp-8); }
details > summary::-webkit-details-marker { display: none; }
details > summary::marker { content: ""; }
.chev { flex: 0 0 auto; color: var(--faint); transition: transform .15s ease; }
details[open] > summary .chev { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .chev { transition: none; } }

/* We publish the formula; the lead-magnet graders do not. */
.formula { margin: 0 0 22px; }
.formula summary { cursor: pointer; color: var(--muted); font-size: var(--fs-15); }
.formula p { color: var(--muted); font-size: var(--fs-15); line-height: 1.6; margin-top: var(--sp-8); max-width: 76ch; }

/* Which week the two first-party categories are read from. A button that
   opens a small list, not the operating system's own select box. In the
   scorecard's header it is the date itself, with a chevron: "Scored 8 Sep". */
.picker { position: relative; }
.picker > button { display: inline-flex; align-items: center; gap: 10px; height: 32px; padding: 0 10px 0 12px; border: 1px solid var(--rule); border-radius: var(--radius-ctl); background: var(--card); color: var(--text); font-size: var(--fs-15); cursor: pointer; min-width: 200px; justify-content: space-between; }
.picker > button:hover { border-color: var(--faint); }
.picker > button .caret { display: inline-flex; color: var(--faint); flex: 0 0 auto; }
.picker > button .caret .chev { transform: rotate(90deg); }
.picker.inline > button { min-width: 0; height: 24px; padding: 0 6px 0 8px; gap: 5px; border-color: transparent; background: none; font-size: var(--fs-13); color: var(--faint); }
.picker.inline > button:hover { border-color: var(--rule); background: var(--card); color: var(--text); }
.picker.inline ul { left: auto; right: 0; }
.picker ul { position: absolute; z-index: 5; top: calc(100% + 4px); left: 0; min-width: 100%; margin: 0; padding: var(--sp-4); list-style: none; background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius-ctl); box-shadow: var(--shadow-md); max-height: 280px; overflow-y: auto; }
.picker li { padding: 7px 10px; border-radius: var(--radius-ctl); font-size: var(--fs-15); color: var(--text); white-space: nowrap; cursor: pointer; }
.picker li[aria-selected="true"] { font-weight: 600; }
.picker li.here, .picker li:hover { background: var(--accent-tint); }

/* A category and its checks. */
.cat { border-top: 1px solid var(--hairline); padding-top: var(--sp-16); margin-top: 26px; scroll-margin-top: var(--sp-16); }
.cat .cathead { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-16); margin-bottom: var(--sp-6); }
.cat .cathead h2 { font-family: var(--sans); font-size: var(--fs-17); font-weight: 600; letter-spacing: -.01em; }
.checks { border-top: 1px solid var(--hairline); }
/* Every row is the same two slots: a 90px status column, then the label, the
   evidence and the fix stacked in one block. Nothing is ever centred against
   a taller neighbour, so the rows keep a rhythm. */
.chk { display: grid; grid-template-columns: 90px minmax(0, 1fr); gap: 14px; padding: 12px 2px; border-bottom: 1px solid var(--hairline); align-items: start; }
.chk:last-child { border-bottom: 0; }
.chk > .s { padding-top: 1px; }
.chk .l { display: block; font-size: var(--fs-16); font-weight: 600; line-height: 1.4; }
.chk .e { display: block; font-size: var(--fs-16); color: var(--muted); margin-top: 3px; line-height: 1.55; max-width: 74ch; }
/* The fix is a second line of the same block, in ink, named as a fix. No
   quote rule: it is ours, not something pasted in. */
.chk .f { display: block; font-size: var(--fs-16); margin-top: 5px; color: var(--text); max-width: 74ch; line-height: 1.55; }
.chk .f::before { content: "Fix: "; font-weight: 600; }
.chk.neutral .l { color: var(--muted); font-weight: 400; }
/* Footnotes, everywhere the same: a real <sup> reference numeral, linked, at
   11px. The pill version read as a stray chip because nothing on the page
   listed what the numbers meant; the pane now ends in a Sources list, so the
   numerals can go back to being footnote markers. */
.srcs { display: inline; margin-left: 1px; }
.src { font-size: var(--fs-13); line-height: 0; color: var(--faint); padding: 0 1px; font-variant-numeric: tabular-nums; }
.src a { color: inherit; text-decoration: none; }
.src a:hover { color: var(--text); text-decoration: underline; }

/* The moves. Each one is shaped the same way: what to do, why it should work,
   and one inline meta line. The two-cell table this used to be left a void
   down its right side and made two short facts look like a report. */
ol.moves { padding-left: 20px; margin: 0; max-width: 68ch; }
ol.moves li { margin-bottom: 20px; font-size: var(--fs-16); line-height: 1.55; }
ol.moves li:last-child { margin-bottom: 0; }
ol.moves b { display: block; font-weight: 600; }
ol.moves span { display: block; color: var(--muted); font-size: var(--fs-15); margin-top: 3px; }
ol.moves .meta { display: block; margin-top: var(--sp-6); color: var(--muted); font-size: var(--fs-15); }

/* The root: one row per company. The read is capped at two lines with a clean
   ellipsis, so five rows keep one rhythm instead of ranging from one line to
   five. The unit is said once, in the column header, not after every numeral. */
table.tbl.companies td { vertical-align: top; }
.companies td .co { font-size: var(--fs-16); font-weight: 600; text-decoration: none; color: var(--text); }
.companies td .co:hover { text-decoration: underline; }
.companies td .lead { display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; color: var(--muted); font-size: var(--fs-15); max-width: 62ch; white-space: normal; line-height: 1.5; }
.companies td .mark { color: var(--bad); font-size: var(--fs-15); display: flex; align-items: center; gap: var(--sp-6); margin-top: var(--sp-4); }
.companies td .mark i { width: 7px; height: 7px; border-radius: 50%; background: var(--dot-bad); flex: 0 0 auto; }
/* U3 goal 1: a small "View as client" link on hover/focus, so the list
   itself is a way in, not just a link to the company page. Always visible
   at narrow widths, where hover has no meaning. */
.companies td .vac { font-size: var(--fs-13); color: var(--muted); text-decoration: none; margin-left: 10px; opacity: 0; }
.companies tr:hover td .vac, .companies td .vac:focus-visible { opacity: 1; }
.companies td .vac:hover { color: var(--text); text-decoration: underline; }
@media (max-width: 700px) { .companies td .vac { opacity: 1; } }
/* display is restated because the legacy .score rule further up this file
   turns anything with that class into a flex box. A flex table cell stops
   filling its row and paints its border-bottom half way up it, which is the
   stray line that used to sit under every score. */
.companies th.score, .companies td.score { display: table-cell; width: 104px; }
.companies td.score { text-align: right; font-variant-numeric: tabular-nums; }
.companies th.auto, .companies td.auto { width: 132px; }
.companies td.auto { font-size: var(--fs-15); color: var(--muted); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.companies td.auto b { font-weight: 600; color: var(--text); }
.companies td.auto .of { color: var(--faint); }
/* A column that says the same thing on every row is not carrying information
   at this scale: it collapses to one check, and the tooltip holds the detail
   until the numbers actually differ. */
.companies td.auto .tick { display: inline-flex; color: var(--faint); }

/* One card header for every card on the Data tab: a label, a caption under it,
   and the card's own 16px padding. The three cards used to carry three
   different header shapes. */
.chead { padding: 0 0 12px; }
.chead .lbl { display: block; font-size: var(--fs-16); font-weight: 600; letter-spacing: -.005em; }
.chead .cap { display: block; font-size: var(--fs-15); color: var(--muted); margin-top: var(--sp-2); max-width: 76ch; }

/* The drop zone takes the card's width and its caption sits under it, so the
   zone and the note about the last upload stop competing side by side. */
.upcard .drop { padding: var(--sp-16); }
.upcard .drop + .help { margin-top: var(--sp-8); }

/* A note that would wreck a table row goes behind a one-line disclosure in the
   cell instead, so the row keeps the height of every other row. */
.rowdet { margin-top: var(--sp-2); }
.rowdet > summary { font-size: var(--fs-13); color: var(--faint); cursor: pointer; }
.rowdet > summary:hover { color: var(--text); }
.rowdet .d { display: block; font-size: var(--fs-15); color: var(--muted); margin-top: 5px; max-width: 56ch; white-space: normal; line-height: 1.5; }

/* A Runs ledger row's own scrape audit (worker/src/admin/server.ts's
   GET /jobs/:id/audit): the summary is the row's own title, not a small
   "Details" aside, so it reads at the same size as the rest of the table;
   the audit's pages and observations tables get room to breathe below it. */
.rowdet.audit > summary { font-size: var(--fs-15); color: var(--text); }
.rowdet.audit > summary:hover { color: var(--accent); }
.run-audit { margin-top: 10px; }

/* Source cards: name, state, when it was last used. */
.card .cardhead .srcname { display: flex; flex-direction: column; gap: var(--sp-2); }
.card .cardhead .srcname .ago { font-size: var(--fs-15); color: var(--faint); font-weight: 400; }
.pill.on-pill { font-weight: 500; }

/* The scan card (docs/scan-progress.md, docs/scan-ux.md). While a scan runs
   this is the whole page: a header that names the company and says where the
   scan is, a bar that moves continuously, a live feed of what is happening,
   and the ten steps as a backbone beside it. */
.card.scan { padding: 20px 22px 18px; }
.card.scan .scanhead { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: 14px; }
.card.scan .scanhead h2 { font-size: var(--fs-17); font-weight: 500; color: var(--muted); letter-spacing: -.01em; }
.card.scan .scanhead h2 .who { font-weight: 600; color: var(--text); }
.card.scan .scanmeta { font-size: var(--fs-15); color: var(--faint); font-variant-numeric: tabular-nums; }
.card.scan .scanwhy { font-size: var(--fs-16); color: var(--text); margin: 0 0 14px; max-width: 62ch; }

.card.scan .bar { height: 6px; background: var(--accent-tint); border-radius: var(--radius-pill); overflow: hidden; margin: 0 0 16px; }
/* linear, not eased: an eased bar looks like it is stalling at the end of
   every poll. The duration matches the tick so the fill is always catching a
   target it is about to be given again. */
.card.scan .bar i { display: block; height: 100%; background: var(--text); border-radius: var(--radius-pill); transition: width .16s linear; }
.card.scan.failed .bar i { background: var(--bad); }

.scanbody { display: grid; grid-template-columns: minmax(0, 1fr) 268px; gap: 28px; align-items: start; }

/* The feed: newest at the bottom, the current line held apart from the rest.
   The mask gradient's #000 is alpha only (mask-image reads luminance/alpha,
   never paints a color) so it stays literal in both themes; a documented
   exception. */
.scanfeed { list-style: none; margin: 0; padding: 0 4px 0 0; height: 236px; overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; mask-image: linear-gradient(to bottom, transparent 0, #000 22px); -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 22px); }
.scanfeed li { display: grid; grid-template-columns: 38px minmax(0, 1fr); gap: 10px; padding: 3px 0; font-size: var(--fs-15); line-height: 1.45; color: var(--faint); }
.scanfeed li .t { font-family: var(--mono); font-size: var(--fs-13); color: var(--faint); opacity: .6; text-align: right; padding-top: var(--sp-2); font-variant-numeric: tabular-nums; }
.scanfeed li .x { min-width: 0; overflow-wrap: anywhere; }
.scanfeed li code { font-family: var(--mono); font-size: var(--fs-15); background: var(--accent-tint); border-radius: var(--radius-sm); padding: 1px 5px; color: var(--text); }
.scanfeed li:nth-last-child(2) { color: var(--muted); }
.scanfeed li.now { color: var(--text); font-weight: 500; }
.scanfeed li.now .t { opacity: 1; }
.scanfeed li.now .x::after { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--text); margin-left: var(--sp-8); vertical-align: baseline; animation: scanpulse 1.2s ease-in-out infinite; }
.scanfeed li:last-child { padding-bottom: var(--sp-4); }

/* The backbone: where the scan is in the ten steps, at a glance. */
.scansteps { list-style: none; margin: 0; padding: 0 0 0 20px; border-left: 1px solid var(--hairline); display: grid; gap: 1px; }
.scansteps li { display: grid; grid-template-columns: 12px minmax(0, 1fr); gap: 10px; align-items: baseline; padding: 3px 0; font-size: var(--fs-15); color: var(--faint); }
.scansteps li .d { width: 8px; height: 8px; border-radius: 50%; border: 1.5px solid var(--rule); background: transparent; position: relative; top: -1px; }
.scansteps li.done .d, .scansteps li.skipped .d { background: var(--dot-ok); border-color: var(--dot-ok); }
.scansteps li.skipped .d { background: var(--faint); border-color: var(--faint); }
.scansteps li.failed .d { background: var(--dot-bad); border-color: var(--dot-bad); }
.scansteps li.running .d { border-color: var(--text); background: var(--text); animation: scanpulse 1.2s ease-in-out infinite; }
.scansteps li.done .l { color: var(--muted); }
.scansteps li.now .l, .scansteps li.running .l { color: var(--text); font-weight: 600; }
.scansteps li .n { display: block; grid-column: 2; font-size: var(--fs-13); color: var(--faint); opacity: .8; margin-top: -1px; }
.scansteps li .n:empty { display: none; }
.scansteps li.now .n { opacity: 1; }

.card.scan.failed { border-color: var(--bad-border); }
.card.scan .btnrow { margin: 14px 0 0; }
.card.scan .help { margin: 16px 0 0; }

@keyframes scanpulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(17,24,39,.25); } 50% { box-shadow: 0 0 0 4px rgba(17,24,39,0); } }
@media (prefers-reduced-motion: reduce) {
  .scansteps li.running .d, .scanfeed li.now .x::after { animation: none; }
  .card.scan .bar i { transition: none; }
}
@media (max-width: 900px) { .scanbody { grid-template-columns: 1fr; gap: 20px; } .scansteps { padding-left: 0; border-left: 0; } }

/* The read arriving after a scan: the one thing, then the tiles one at a
   time, then the rest (docs/scan-ux.md, principle 5). Motion is the only
   thing staged here — with reduced motion the whole page is simply there. */
@media (prefers-reduced-motion: no-preference) {
  .reveal-on .rv { opacity: 0; animation: revealin .34s cubic-bezier(.2, .7, .3, 1) both; }
}
@keyframes revealin { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* View as (docs/client-surface.md section 5, web/admin/viewas.js): the
   persistent banner and the role-preview panel. The preview itself is
   styled with "vp-" classes that mirror web/app.html's own Home layout
   closely enough to read as the same page, without pulling that page's
   inline stylesheet into admin.html — see viewas.js's header for why it
   is a parallel render rather than a shared one. */
.viewas-banner { display: none; align-items: center; justify-content: space-between; gap: var(--sp-16); position: fixed; top: 0; left: 0; right: 0; z-index: 60; padding: 9px 20px; background: var(--warn-bg); border-bottom: 1px solid var(--warn); color: var(--text); font-size: var(--fs-15); }
.viewas-banner.on { display: flex; }
.viewas-banner span { font-weight: 500; }
body.viewas-active { padding-top: 42px; }

.viewas-role-row { display: flex; flex-wrap: wrap; gap: var(--sp-8); margin: 4px 0 20px; }
.people-form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-8); margin-top: var(--sp-12); }
.people-form input[type="text"] { width: auto; flex: 1 1 220px; }
.people-form select { width: auto; flex: 0 0 auto; }
.vp-intro { color: var(--muted); font-size: var(--fs-15); line-height: 1.6; max-width: 640px; margin-bottom: var(--sp-4); }
.invite-card { margin-top: var(--sp-16); max-width: 560px; }
table.tbl td.nowrap { white-space: nowrap; }
.invite-card h3 { font-size: var(--fs-15); font-weight: 600; margin-bottom: var(--sp-8); }
.invite-card textarea { margin-bottom: 10px; }
.invite-warn { color: var(--warn); font-size: var(--fs-15); line-height: 1.5; margin-bottom: 10px; }
.viewas-frame { border: 1px solid var(--rule); border-radius: var(--radius-ctl); padding: 28px; background: var(--card); max-width: 720px; }

.vp-wrap h1 { font-family: var(--sans); font-weight: 600; font-size: var(--fs-24); letter-spacing: -.01em; margin-bottom: var(--sp-4); }
.vp-wrap .vp-who { color: var(--muted); font-size: var(--fs-13); margin-bottom: var(--sp-24); }
.vp-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--hairline); border: 1px solid var(--hairline); border-radius: var(--radius); overflow: hidden; margin-bottom: 14px; }
.vp-kpis .stat { background: var(--card); padding: 12px 14px; }
.vp-kpis .stat .n { font-size: var(--fs-24); font-weight: 600; letter-spacing: -.01em; line-height: 1.1; }
.vp-kpis .stat .l { font-size: var(--fs-13); color: var(--muted); margin-top: var(--sp-4); }
.vp-sec { background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius); padding: 16px 18px; margin-bottom: var(--sp-12); }
.vp-sec h2 { font-size: var(--fs-13); font-weight: 600; letter-spacing: .02em; color: var(--muted); margin-bottom: var(--sp-12); }
.vp-sec .vp-note-body { font-size: var(--fs-15); line-height: 1.6; white-space: pre-wrap; }
.vp-sec .vp-src { color: var(--faint); font-size: var(--fs-13); margin-top: var(--sp-8); }
.vp-row { font-size: var(--fs-15); padding: 3px 0; display: flex; justify-content: space-between; gap: var(--sp-16); max-width: 420px; }
.vp-row .k { color: var(--muted); }
.vp-row .v { font-weight: 500; }
.vp-rls { color: var(--faint); font-size: var(--fs-13); margin-top: 20px; border-top: 1px solid var(--hairline); padding-top: var(--sp-12); }

.vp-status .line { display: grid; grid-template-columns: 14px 100px minmax(0, 1fr); gap: var(--sp-12); align-items: baseline; padding: 7px 0; font-size: var(--fs-15); }
.vp-status .line .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--faint); position: relative; top: -1px; }
.vp-status .line .dot.good { background: var(--good); }
.vp-status .line .who { font-weight: 500; }
.vp-status .line .what { color: var(--muted); }
.vp-status .line .what.good { color: var(--text); }

/* ---- Connections (U5, 2026-09-27): a compact list, then one detail page
   per row. Each row is its own line, not a card, so six rows and two links
   fit one 1280 screen with room to spare. ---- */
.conn-list td { padding: 14px 16px; }
.conn-list tr.rowlink { cursor: pointer; }
.conn-name { font-size: var(--fs-16); font-weight: 600; text-decoration: none; color: var(--text); display: block; }
.conn-name:hover { text-decoration: underline; }
.conn-line { display: block; color: var(--muted); font-size: var(--fs-15); margin-top: var(--sp-2); }
.conn-links { display: flex; gap: 18px; margin-top: var(--sp-4); }
.back-link { display: inline-block; color: var(--muted); font-size: var(--fs-15); text-decoration: none; margin-bottom: var(--sp-16); }
.back-link:hover { color: var(--text); }
.conn-head { display: flex; align-items: center; gap: var(--sp-12); margin-bottom: var(--sp-6); }
.conn-head h1 { font-size: var(--fs-24); font-weight: 600; letter-spacing: -.01em; margin: 0; }
.conn-lead { color: var(--muted); font-size: var(--fs-15); margin: 0 0 18px; }

/* ---- The admin rebuild (2026-09-27, ADMIN.md) -------------------------- */
/* Side menu by job. The Build group sits at the bottom, quieter, because
   it describes the product rather than the business. */
.rail .rail-group { display: flex; flex-direction: column; gap: 1px; margin-top: var(--sp-16); }
.rail .brand + .rail-group { margin-top: var(--sp-24); }
.rail .rail-group.quiet .rail-link { color: var(--muted); font-size: var(--fs-15); }
.rail .rail-group.quiet .section { padding-top: 0; }
/* Companies: the add form is the screen's one action. */
#add-client { margin: 0; }
#add-client input { height: 32px; font-size: var(--fs-15); padding: 0 10px; border: 1px solid var(--rule); border-radius: var(--radius-ctl); background: var(--card); color: var(--text); width: 200px; }
.empty-line { color: var(--muted); font-size: var(--fs-15); margin: 0; }
/* View as client: a bar of controls over the framed client app. */
.va-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-12); margin-bottom: 14px; }
.va-field { display: flex; flex-direction: column; gap: var(--sp-4); font-size: var(--fs-13); color: var(--muted); }
.va-field select { height: 32px; font-size: var(--fs-15); padding: 0 8px; border: 1px solid var(--rule); border-radius: var(--radius-ctl); background: var(--card); color: var(--text); min-width: 200px; }
.va-width { display: inline-flex; gap: var(--sp-4); }
.va-width .btn.on { background: var(--accent-tint); color: var(--text); border-color: var(--accent); }
.va-exit { margin-left: auto; }
.va-frame { border: 1px solid var(--hairline); border-radius: var(--radius); overflow: hidden; background: var(--card); margin: 0 auto; }
.va-frame iframe { display: block; width: 100%; height: 78vh; border: 0; }
.va-frame.phone { width: 390px; max-width: 100%; }
@media (max-width: 700px) {
  /* The menu becomes a wrapped row of links, not a tall list over the page. */
  .rail { max-height: none; padding: var(--sp-12); }
  .rail .rail-group { flex-direction: row; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-8); }
  .rail .brand + .rail-group { margin-top: 10px; }
  .rail .spacer, .rail .rail-group .section { display: none; }
  .rail .rail-link { min-height: 44px; }
  #add-client { width: 100%; display: flex; gap: var(--sp-8); }
  #add-client input { flex: 1; width: auto; min-width: 0; }
  .va-field, .va-field select { width: 100%; min-width: 0; }
  .va-exit { margin-left: 0; }
}
