/* Ledger — light product theme (Phase 6, 2026-09-14). Every one of the ten
   Mobbin references the pivot doc names is a light interface
   (reviews/MOBBIN_PIVOT_REFS.md); D4 ("keep the dark token system") is
   withdrawn. The palette lives ONLY in this block — every rule below reads a
   token, and the Phase 6 tests fail on a hex anywhere else. Variable NAMES
   are the ones the rules already used, re-valued for a light ground. */
:root {
  color-scheme: light;
  /* Phase 15 (24 Sep 2026, PHASE15_BRIEF.md A1): the brand refresh off the
     mockup (reviews/phase15_mockup.png). The ground turns cool, the one
     accent turns BLUE with white ink (this closes the Phase 6 open question
     "accent green vs old brand blue": blue), and cards float. Same variable
     NAMES as Phase 6, re-valued; the palette still lives only in this block. */
  --bg: #F4F4F5;          /* the ground: the brand sheet's off-white (brand/BRAND.md, 18 Oct 2026) */
  --panel: #FFFFFF;       /* a sheet, a card */
  --panel2: #ECECFE;      /* the lit surface: the active nav tile, the account menu (the brand blue at 8%) */
  --panel2-line: rgba(24, 20, 248, .16);  /* the 1px blue-tinted hairline round the lit tile */
  --raise: #E8EDF5;       /* the bar track and a pressed control */
  --line: rgba(16, 24, 40, .08);   /* the hairline */
  --soft: rgba(16, 24, 40, .06);   /* the quieter hairline; also the faint tint under a URL row */
  --scrim: rgba(16, 24, 40, .32);  /* behind a sheet */
  /* Text. Measured with the WCAG 2.1 relative-luminance formula against
     every surface above (tests/test_ledger_pivot_phase6.py pins it; the
     floor never moves: 7:1 body, 4.5:1 everything else). Phase 15C values
     (the official brand pack, brand_assets/README.md), measured 24 Sep:
     --hi 15.19:1 on the bar track (its worst surface), --mid 6.54:1,
     --low 5.24:1 (both on the bar track, their worst). */
  --hi: #0D0D0F;          /* brand Ink (18 Oct 2026): near-black, all primary text and the wordmark; 19.3:1 on white */
  --mid: #475467;
  --low: #586273;
  /* ---- Phase 15C: the official brand pack (brand_assets/, 24 Sep) ----
     Brand values used EXACTLY where the pack puts them, and only as data or
     decoration, never as text or a button fill:
       Signal blue #3B82F6 (--signal): the score ring, the sparkline, the
         nav icons, the Google impact bar (--ch-1). White on it is 3.68:1,
         so it never carries text and is never a button.
       Electric violet #8B5CF6 stays in the pack's ARTWORK (the mark's
         gradient); the AI impact bar is one step deeper, #6D28D9 (--ch-3,
         below), so it can sit beside the blue bar. Never text.
       Signal cyan #25D7E7 and AI teal #16C8C8 live ONLY inside the pack's
         SVG artwork (the mark, the AI card background and network), so they
         are not tokens here: no CSS rule may paint them.
       Card surface #EDF6FF (--ai-card): the AI visibility card's base under
         its artwork. */
  /* the lockup's travelling light (restored 18 Oct 2026): the same three colours as the 25 Sep light */
  --wm-light-a: #6366F1;
  --wm-light-b: #22D3EE;
  --wm-light-c: #DDD6FE;
  --signal: #1814F8;      /* 18 Oct 2026: the one brand blue (the tag mark's colour), 8.4:1 with white, so it may carry text and fill a button */
  --ai-card: #EDF6FF;
  /* The lockup (mark + word "seolution"), sized ONCE from this pair
     (P15 fold-in, lead's decision on the wordmark, 24 Sep). The pack's
     seolution-logo.svg is a PRINT lockup (word ink 0.37 of the mark's), which
     beside a 24px mark would leave a ~9px unreadable word; the app keeps the
     mockup's proportion instead: the word about the height of the mark.
     Full lockup (sidebar, sign-in): the pair as is, 28px mark, 24px word.
     Compact lockup (top bar, public nav): the SAME pair x 6/7 (24px mark,
     20.57px word), so every lockup has one ratio, word / mark = 6 / 7. */
  --lockup-mark: 28px;
  --lockup-word: 24px;
  /* THE one accent: brand hue, darkened for text. Signal blue's natural
     darker step, so white ink clears the floor (5.17:1). The primary
     button, the selected segment, the numbered rank circle and the avatar.
     Links and "on" text read --note. */
  --accent: #1814F8;      /* the brand blue; white on it is 8.4:1 */
  --accent-ink: #FFFFFF;
  /* The AI visibility number: AI teal's hue, darkened for text (5.18:1 on
     the card surface #EDF6FF, 5.65:1 on white; the lowest measured on the
     rendered card artwork under the text is in PHASE15C_DONE.md). */
  --ai: #0B7373;
  /* the "Last audited" chip's dot: decorative, the words beside it carry
     the meaning */
  --live: #12B76A;
  /* Status = a tiny pale pill with dark text of the same hue (rose / amber
     / green / blue), never a full row and never a bright fill. Each ink is
     >= 4.5:1 on white AND on its own tint (measured 24 Sep: crit 5.49 on
     tint / 6.48 on white, warn 5.25 / 5.93, note 5.70 / 6.70, good 4.61 /
     5.38). --note is Signal blue's hue darkened for text: the brief's
     #2563EB measured 4.39:1 on its own tint (#E4EDFF), under the floor, so
     it is one step darker, #1D4ED8. */
  --crit: #B4233A;  --crit-bg: #FDE7EA;
  --warn: #8A5A00;  --warn-bg: #FDF0D5;
  --note: #1814F8;  --note-bg: #ECECFE;   /* 18 Oct 2026: links and 'on' text in the brand blue (8.4:1 on white, 7.1:1 on its tint) */
  --good: #1E7A3C;  --good-bg: #DDF3E4;

  /* ---- chart palette, re-picked for the light ground (Phase 6) ----
     Phase 15C: --ch-1 is Signal blue #3B82F6 (Google organic). The brand's
     Electric violet #8B5CF6 beside it FAILED the dataviz skill's validator
     as a pair (CVD deltaE 1.3 deutan, normal vision 12.0).
     P15 fold-in (lead's decision, 24 Sep): the AI bar is #6D28D9, the
     violet one step deeper; the artwork keeps #8B5CF6. Re-run with the
     validator (`--mode light --surface #ffffff`), 24 Sep:
       the two bars as a PAIR #3B82F6,#6D28D9: ALL PASS (CVD deltaE 10.9
         deutan, tritan 18.3; normal vision 18.7; contrast 3.68 / 7.10 on
         white);
       the full order #3B82F6,#EB6834,#6D28D9,#008300: ALL PASS (worst
         adjacent CVD 28.2 protan, normal vision 34.5).
     The bars still never rely on colour alone: each carries its own label
     ("Google organic" / "AI visibility") and its number beside it.
     The UI inks above are never a series; charts read these. Status is its
     own fixed trio, reserved, always with a label beside it. */
  --ch-1: #1814F8;   /* the brand blue: Google organic (18 Oct 2026) */
  --ch-2: #EB6834;   /* orange */
  --ch-3: #0B7373;   /* the AI teal (the same ink as --ai): AI visibility. Blue and teal sit two hue families apart; each bar still carries its label */
  --ch-4: #008300;   /* green */
  --ch-good: #008300;
  --ch-warn: #B45309;
  --ch-bad: #D03B3B;
  --bar-google: var(--ch-1);
  --bar-ai: var(--ch-3);

  /* The face (18 Oct 2026, brand/BRAND.md §7): Outfit, SELF-HOSTED (static/fonts/, the OFL variable woff2,
     preloaded by both shells, font-display: swap) with the system stack as the fallback. No third-party
     font host, ever. Mono is kept only for things that ARE code: a URL list, a pairing code, a property id,
     and the first screen's code line. */
  --font: "Outfit", -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  /* ---- the type scale (Phase 5), display step brought to the references'
     24-28px: caption, small, body, lead, title, page, display ----
     Phase 15 adds the section title (20px) and the hero number (44px). */
  --fs-1: 12px;  --fs-2: 13.5px;  --fs-3: 15px;  --fs-4: 17px;
  --fs-5: 19px;  --fs-6: 25px;  --fs-7: 29px;  /* 18 Oct 2026: one step up for Outfit's smaller x-height (480 vs Inter's 546 per 1000) */
  --fs-section: 20px;  --fs-hero: 44px;
  /* ONE soft shadow (Phase 15): the cards float on the cool ground, so the
     rule is now "cards float, rows inside a card do not". Menus, sheets,
     toasts and the sticky bar keep the same one shadow. */
  --shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 8px 24px rgba(16, 24, 40, .05);
  --radius-card: 16px;
  --radius-ctl: 10px;
  /* every fade / transform runs at this — the references move nothing
     beyond a 150ms fade; reduced motion collapses it (below) */
  --motion: 150ms ease;
}
/* The ONE self-hosted face (18 Oct 2026, brand/BRAND.md §7): Outfit, the geometric
   sans the wordmark is cut from, as its variable woff2 (weights 100-900 in one
   file, Latin subset, 32 KB, SIL OFL 1.1: the licence sits beside it in
   static/fonts/). Served from this app's own /static, never a third-party font
   host; `swap` so the system stack paints first. Both shells preload it. */
@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/outfit-latin-wght-normal.woff2") format("woff2");
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body {
  background: var(--bg); color: var(--hi);
  font: 15px/1.5 var(--font);
}
.display { font-family: var(--font); font-weight: 600; letter-spacing: -.01em; }
.mono { font-family: var(--font-mono); }
a { color: var(--note); text-decoration: none; }
button { font: inherit; cursor: pointer; }
/* G12 (fix round 2): ONE focus ring for everything focusable — buttons,
   fields, links AND a <summary> (the account menu, the ⋮ menu) — in the
   text colour. The only `:focus-visible` outline in the file. */
:focus-visible { outline: 2px solid var(--hi); outline-offset: 2px; }
/* B-2 (Phase 13, D3): "the right side scroll bar I can't see it... I try to
   scroll up and down and I can't scroll up and down easily." Two faults, one
   line each: (a) the thumb was `--line`, a hairline at 8% black, on a page
   barely lighter than it — no contrast at all, and Firefox had no rule at
   all. `--mid` is already measured at 5.7:1 on this ground (the Phase 6
   pin), so the scrollbar reuses it rather than inventing a token this
   file's own contrast tests do not know about. (b) `.ask-thread`'s
   `overscroll-behavior: contain` (below) stopped the wheel ever reaching the
   page once the pointer sat over the chat mid-page; Phase 13 moves that
   thread into a fixed floating panel, where trapping the wheel is the
   correct behaviour (a panel over the page, not part of its flow) — see the
   `#ask-panel` block above. The page's own scroller carries no such rule and
   never has. */
html { scrollbar-width: auto; scrollbar-color: var(--mid) var(--raise); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--raise); }
::-webkit-scrollbar-thumb { background: var(--mid); border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: var(--hi); }
/* The page scrollbar reads slightly wider than an inner one (12px vs 10px) —
   the one the eye has to find "at a glance" per the brief. */
html::-webkit-scrollbar, body::-webkit-scrollbar { width: 12px; height: 12px; }
.pulse { animation: pulse 1.4s ease-in-out infinite; }
.fade { animation: fade 150ms ease both; }
@keyframes fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes pulse { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .fade, .pulse { animation: none !important; }
  /* Phase 5: one rule for every surface — public.css rides on this file, so
     its wait-state pulses and skeleton sheen collapse here too. Anything
     that still moves jumps to its end state instead of animating. */
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* top bar */
.topnav {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 20px; border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.topnav .brand { font-size: 16px; font-weight: 700; color: var(--hi); }
.topnav .crumb { font-family: var(--font-mono); font-size: 11px; color: var(--low); }
.topnav .crumb-domain { font-family: var(--font-mono); font-size: 12px; color: var(--mid); }
.topnav .spacer { flex: 1; }
/* who you are and how to leave stay together, so a narrow screen never orphans
   "sign out" onto a line of its own */
.topnav .whoami { display: inline-flex; align-items: center; gap: 12px; white-space: nowrap; }
.topnav .who { font-family: var(--font-mono); font-size: 11px; color: var(--low); }
.topnav .signout { font-family: var(--font-mono); font-size: 11px; color: var(--low); }
/* The account menu (B5, 2026-09-03) — one disclosure instead of a row of
   links competing with the domain title on a narrow header. */
.acct-menu { position: relative; }
.acct-menu > summary.who {
  cursor: pointer; list-style: none; font-family: var(--font-mono);
  font-size: 11px; color: var(--low); white-space: nowrap; padding: 4px 2px;
}
.acct-menu > summary.who::-webkit-details-marker { display: none; }
.acct-menu > summary.who::after { content: ' ▾'; }
.acct-menu-b {
  position: absolute; top: 100%; right: 0; z-index: 20; margin-top: 4px;
  background: var(--panel2); border: 1px solid var(--line); border-radius: 8px;
  padding: 6px; display: flex; flex-direction: column; gap: 2px; min-width: 150px;
  box-shadow: var(--shadow);
}
.acct-menu-b a { padding: 7px 9px; border-radius: 5px; white-space: nowrap; }
.acct-menu-b a:hover { background: var(--raise); }
/* G9: the name repeated inside the open menu body — see the template
   comment on `.acct-menu-name`. */
.acct-menu-name {
  padding: 7px 9px 8px; margin-bottom: 2px; border-bottom: 1px solid var(--line);
  font-size: 13px; font-weight: 600; color: var(--hi);
}
/* The credits balance in the account menu is mobile's home for it (redesign
   brief section 1) — desktop already pins it to the bottom of the sidebar
   (`.pnav-credits`, hidden under 901px in the same media query below), so
   the two never both show. */
.acct-credits { color: var(--note); font-weight: 600; border-top: 1px solid var(--line); margin-top: 2px; padding-top: 9px !important; }
@media (min-width: 901px) { .acct-credits { display: none; } }

/* the tab strip scrolls on narrow screens — fade the right edge so a clipped
   tab reads as "there is more", not as a cut-off label */
.tabs { position: relative; -webkit-overflow-scrolling: touch; }
/* The right-edge fade that tells a clipped tab strip "there is more" rather
   than "this label got cut off". Always in the DOM, only visible once the
   strip can actually overflow (mobile), so it never floats over a desktop
   row that has nothing to hide. */
.tabs::after {
  content: ''; position: absolute; top: 0; right: 0; bottom: 0; width: 28px;
  pointer-events: none; display: none;
  background: linear-gradient(to right, transparent, var(--bg));
}
@media (max-width: 760px) {
  /* Scrolls horizontally, never wraps (B5, 2026-09-03, UX review: "feels like
     a cramped website, not an app" — two-row tabs was the old fix and it was
     the wrong one; an app's tab strip scrolls). The fade above marks the
     clipped edge so a tab ending mid-word still reads as "swipe for more",
     not as broken. */
  .tabs { flex-wrap: nowrap; overflow-x: auto; }
  .tabs::after { display: block; }
  .tabs a { padding: 10px 9px; flex-shrink: 0; }
}

/* shared atoms */
.label { font-family: var(--font-mono); font-size: var(--fs-1); color: var(--low); letter-spacing: .1em; text-transform: uppercase; margin-bottom: 8px; }
.chip {
  font-family: var(--font-mono); font-size: var(--fs-1);
  border: 1px solid var(--line); border-radius: 4px; padding: 2px 7px; white-space: nowrap;
}
.chip.filled { color: var(--hi); border-color: transparent; }
/* R2: a question chip is a BUTTON — `<button class="chip">` on Tasks ("What
   should I do first?") and every preset on Results › Ask — and a bare
   <button> paints the browser's own grey control, because the `.chip` rule
   above sets a border and a font but no background and no colour. Same
   surface language as the app's other chips: panel background, hairline
   border, --hi text (12.4:1 on --panel), accent on hover and focus, --low
   (4.65:1) when disabled. Sized as a sentence, not a tag — it is a
   question someone is meant to press. */
button.chip {
  cursor: pointer; font-family: inherit; font-size: 12px; line-height: 1.35;
  padding: 6px 10px; border-radius: 5px; color: var(--hi);
  background: var(--panel); border: 1px solid var(--line); white-space: normal;
  transition: border-color 120ms ease, background 120ms ease;
}
button.chip:hover:not(:disabled), button.chip:focus-visible { border-color: var(--mid); background: var(--panel2); }
button.chip:disabled { color: var(--low); cursor: default; }
.btn-primary {
  font-family: var(--font); font-size: var(--fs-2); font-weight: 500; color: var(--accent-ink);
  background: var(--accent); border: none; border-radius: 6px; padding: 8px 14px;
  transition: filter var(--motion);
}
.btn-primary:hover:not(:disabled) { filter: brightness(1.08); }
.btn-ghost {
  font-family: var(--font-mono); font-size: var(--fs-2); color: var(--mid);
  background: transparent; border: 1px solid var(--line); border-radius: 6px; padding: 6px 10px;
  transition: border-color var(--motion), color var(--motion);
}
.btn-ghost:hover:not(:disabled) { border-color: var(--mid); color: var(--hi); }
.btn-block { display: block; width: 100%; text-align: center; }

/* D6, redesign brief — the panel's primary action as two cards, RECOMMENDED
   ribbon on "have us do it" (Revolut Business / LangChain / User Interviews). */
.act-cards { display: flex; flex-direction: column; gap: 8px; }
.act-card { position: relative; border: 1px solid var(--line); border-radius: 8px; padding: 10px; }
.act-card-primary { border-color: var(--note); padding-top: 20px; }
/* C6 (fix round): white-on-accent at 9px measured 2.98:1 — under the 4.5:1
   floor at any size, let alone a size too small to qualify as WCAG "large
   text" anyway. Dark ink (`--bg`) on the same accent background is 6.44:1;
   the size moved to 11px so it reads as body text, not a badge nobody
   needs to squint at. */
.act-ribbon {
  position: absolute; top: -1px; left: 10px; transform: translateY(-50%);
  background: var(--accent); color: var(--accent-ink); font-family: var(--font);
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase; padding: 2px 7px; border-radius: 3px;
}
.act-card button, .act-card a.btn-primary { display: block; width: 100%; text-align: center; }
.act-card-note { margin: 6px 0 0; font-size: 11px; color: var(--low); line-height: 1.4; }

/* D6 — panel footer keyboard hints (Plain). */
.panel-kbd-hint { margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--line);
  font-size: 11px; color: var(--low); }
.panel-kbd-hint kbd { font-family: var(--font-mono); font-size: 10px; color: var(--mid);
  border: 1px solid var(--line); border-radius: 3px; padding: 1px 5px; margin: 0 2px 0 0; }

/* D6 — technical detail (evidence, mock-up) collapsed by default (Midday/Jira). */
.tech-detail { margin-top: 10px; border-top: 1px solid var(--line); padding-top: 10px; }
.tech-detail > summary { cursor: pointer; font-size: 12px; color: var(--mid); font-family: var(--font-mono); }

.good { color: var(--good); } .crit { color: var(--crit); } .warn-c { color: var(--warn); }
.mid-c { color: var(--mid); } .low-c { color: var(--low); }
/* "Connected" has to READ as green. Colouring only the text left it a thin
   grey outline with faint letters inside, which is why a green status word got
   reported as not being green at all. The border and a tinted ground carry it
   with the text. */
.chip.good-c { color: var(--good); border-color: transparent; background: var(--good-bg); }
/* Same treatment for the warning half, and for the same reason: it sits
   directly beside a green "Connected" saying nothing is arriving, so it has to
   be as readable as the word it is qualifying. */
.chip.warn-c { color: var(--warn); border-color: transparent; background: var(--warn-bg); }
.sevdot { width: 6px; height: 6px; border-radius: 999px; display: inline-block; flex-shrink: 0; }

input, select, textarea {
  font: inherit; color: var(--hi); background: var(--bg);
  border: 1px solid var(--line); border-radius: 5px; padding: 8px 10px;
}
textarea { font-size: 12.5px; line-height: 1.6; resize: vertical; width: 100%; }

/* portfolio */
.wrap { padding: 20px; max-width: 1400px; margin: 0 auto; }
.hero-h { font-size: 24px; letter-spacing: -.02em; font-weight: 700; margin: 4px 0 4px; }
.hero-sub { font-size: 13px; color: var(--mid); margin: 0 0 20px; }
.pcards { display: flex; flex-wrap: wrap; gap: 16px; }
.pcard {
  flex: 1 1 320px; max-width: 480px; background: var(--panel); border: 1px solid var(--line);
  border-radius: 9px; padding: 16px; color: var(--hi); display: block;
}
.pcard:hover { border-color: var(--mid); }
.pcard .nm { font-size: 15px; font-weight: 600; }
.pcard .dm { font-family: var(--font-mono); font-size: 11px; color: var(--low); margin-top: 3px; }
.pcard .health { font-family: var(--font-mono); font-size: 26px; line-height: 1; text-align: right; }
.pcard .health-l { font-family: var(--font-mono); font-size: 9.5px; color: var(--low); text-align: right; }
.pcard .meta { display: flex; gap: 12px; margin-top: 12px; flex-wrap: wrap; font-family: var(--font-mono); font-size: 10.5px; color: var(--low); }
.sevbar { display: flex; height: 8px; border-radius: 3px; overflow: hidden; margin-top: 14px; background: var(--soft); }
.sevbar div { height: 100%; }
.sevkey { display: flex; gap: 12px; margin-top: 6px; font-family: var(--font-mono); font-size: 10px; color: var(--low); }

.attn { border: 1px solid var(--line); border-radius: 9px; margin-top: 24px; overflow: hidden; }
.attn-h { padding: 10px 16px; border-bottom: 1px solid var(--line); }
.attn-row {
  display: flex; align-items: center; gap: 12px; padding: 11px 16px; flex-wrap: wrap;
  border-top: 1px solid var(--soft); color: var(--hi);
}
.attn-row:first-of-type { border-top: 0; }
.attn-row:hover { background: var(--panel2); }
.attn-row .pj { font-family: var(--font-mono); font-size: 10.5px; color: var(--low); min-width: 110px; }
.attn-row .tt { font-size: 13.5px; flex: 1; min-width: 220px; }

/* project layout */
.pshell { display: flex; min-height: calc(100vh - 49px); }
.rail { width: 224px; flex-shrink: 0; border-right: 1px solid var(--line); }
.rail .label { padding: 16px 16px 6px; margin: 0; }
.rail-item { display: block; padding: 11px 16px; border-left: 2px solid transparent; color: var(--mid); }
.rail-item.on { background: var(--panel2); border-left-color: var(--note); color: var(--hi); }
.rail-item:hover { background: var(--panel); }
.rail-item .r1 { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.rail-item .r1 .h { font-family: var(--font-mono); font-size: 12px; }
.rail-item .r2 { font-family: var(--font-mono); font-size: 10px; color: var(--low); margin-top: 4px; display: flex; gap: 10px; }
.pmain { flex: 1; min-width: 0; }

/* The shell (redesign brief D3/D11) — Home · Tasks · Results · Settings.
   Desktop: a slim left sidebar (Linear), between the staff project rail (if
   any) and the content. Mobile: a fixed bottom bar, same markup — CSS only. */
.pnav { width: 176px; flex-shrink: 0; border-right: 1px solid var(--line); display: flex; flex-direction: column; padding: 16px 10px; }
.pnav-brand { font-size: 13px; font-weight: 700; color: var(--hi); padding: 4px 8px 14px; letter-spacing: -.01em; }
.pnav-item { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 7px; color: var(--mid); font-size: var(--fs-3); font-weight: 500; transition: background var(--motion), color var(--motion); }
.pnav-item:hover { background: var(--panel); color: var(--hi); }
.pnav-item.on { background: var(--panel2); color: var(--hi); }
.pnav-ic { width: 17px; height: 17px; flex-shrink: 0; }
.pnav-item .n { margin-left: auto; background: var(--raise); color: var(--hi); border-radius: 10px; font-size: 10.5px; font-family: var(--font-mono); padding: 1px 6px; }
.pnav-more { margin-top: 4px; }
.pnav-more > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 7px; color: var(--low); font-size: 13px; }
.pnav-more > summary::-webkit-details-marker { display: none; }
.pnav-more > summary::before { content: '···'; font-weight: 700; letter-spacing: 1px; width: 17px; text-align: center; }
.pnav-more[open] > summary { color: var(--hi); background: var(--panel); }
.pnav-more-b { display: flex; flex-direction: column; gap: 2px; padding: 4px 4px 4px 10px; }
.pnav-more-b a, .pnav-more-b button { text-align: left; font-size: 12.5px; color: var(--mid); padding: 7px 8px; border-radius: 6px; background: none; border: none; font-family: inherit; cursor: pointer; }
.pnav-more-b a:hover, .pnav-more-b button:hover:not(:disabled) { background: var(--panel); color: var(--hi); }
.pnav-more-b button:disabled { color: var(--low); cursor: default; }
.pnav-more-form { margin: 0; }
.pnav-spacer { flex: 1; }
.pnav-credits { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px; margin: 6px 0 0; border-top: 1px solid var(--line); font-family: var(--font-mono); font-size: 12px; color: var(--mid); }
.pnav-credits a { color: var(--note); font-weight: 600; }
.seg-tabs { margin: 0 0 4px; }

/* The phone's fixed bottom nav (`.pnav` under 901px) — measured 56px at 390
   (4px padding either side + the 10px-label items + 1px border) plus the
   home-indicator inset the nav's own padding adds. 0 on desktop, where the
   nav is a sidebar. Anything pinned to the bottom of the viewport (the
   task panel's `.donow-sticky` action bar, project.html) sits ON this, not
   at 0 — at 0 it slid under the nav's z-index:20 and "Brief for my team"
   could not be tapped (R1). The default is declared HERE, above the media
   block that overrides it: same specificity, so source order decides, and
   a default written further down the file silently won (0px at 390). */
:root { --pnav-h: 0px; }

@media (max-width: 900px) {
  .pnav { position: fixed; left: 0; right: 0; bottom: 0; z-index: 20; width: auto;
    flex-direction: row; align-items: stretch; gap: 2px; padding: 4px 4px calc(4px + env(safe-area-inset-bottom));
    background: var(--panel); border-right: 0; border-top: 1px solid var(--line); }
  .pnav-brand, .pnav-more, .pnav-spacer, .pnav-credits { display: none; }
  .pnav-item { flex: 1; flex-direction: column; gap: 2px; padding: 6px 2px; border-radius: 8px; font-size: 10px; text-align: center; }
  .pnav-item .n { margin-left: 0; position: absolute; transform: translate(10px, -6px); }
  :root { --pnav-h: calc(56px + env(safe-area-inset-bottom, 0px)); }
  /* the page's own bottom padding clears the nav, so the last line of any
     section is never under it */
  .pmain { padding-bottom: calc(var(--pnav-h) + 12px); }
}

/* G5 (Grok, fix round): Settings sections are a left sub-nav (Cursor
   reference), not the top segmented strip that stays Results-only — same
   vertical-list language as the shell's own `.pnav-item`, at a smaller
   scale since this one nests inside the page rather than owning the whole
   left edge. */
.settings-shell { display: flex; gap: 26px; align-items: flex-start; }
.settings-subnav {
  width: 152px; flex-shrink: 0; display: flex; flex-direction: column; gap: 2px;
  position: sticky; top: 14px;
}
.settings-subnav a {
  display: block; padding: 8px 10px; border-radius: 7px; color: var(--mid);
  font-size: 13px; font-weight: 500;
}
.settings-subnav a:hover { background: var(--panel); color: var(--hi); }
.settings-subnav a.on { background: var(--panel2); color: var(--hi); }
.settings-content { flex: 1; min-width: 0; }
@media (max-width: 900px) {
  .settings-shell { display: block; }
  .settings-subnav {
    position: static; width: auto; flex-direction: row; overflow-x: auto;
    gap: 4px; margin-bottom: 12px; padding-bottom: 4px;
    border-bottom: 1px solid var(--line);
  }
  .settings-subnav a { white-space: nowrap; }
}

.phead { padding: 20px 24px 16px; border-bottom: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 18px; align-items: flex-end; justify-content: space-between; }
.phead h1 { font-size: var(--fs-6); margin: 0; letter-spacing: -.02em; font-weight: 700; }
.phead .sub { display: flex; gap: 16px; margin-top: 6px; font-family: var(--font-mono); font-size: 11px; color: var(--low); flex-wrap: wrap; }
/* G12 (Grok, fix round): baseline-align the header's stat columns — default
   flex `stretch` let each `.num`'s height track its own content, which
   with an optional 4th column ("fix first", conditional on a critical
   count) let the row disagree with itself about where the numbers sat.
   `flex-end` puts every `.l` caption on the same line regardless of how
   many columns are present. */
.phead .nums { display: flex; gap: 22px; align-items: flex-end; }
.phead .num { text-align: left; }
.phead .num .v { font-family: var(--font-mono); font-size: 20px; }
.phead .num .l { font-family: var(--font-mono); font-size: 9.5px; color: var(--low); }
/* the two audit buttons + whatever they last did */
.phead-acts { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.phead-btns { display: flex; gap: 8px; align-items: center; }
.phead-btns form { margin: 0; }
.phead-btns .btn-primary[disabled] { background: var(--raise); color: var(--mid); cursor: default; }
.phead-note {
  font-family: var(--font-mono); font-size: 10.5px; color: var(--low);
  margin: 0; text-align: right; max-width: 460px; line-height: 1.5;
}
@media (max-width: 700px) {
  .phead-acts { align-items: flex-start; width: 100%; }
  .phead-note { text-align: left; }
  .phead-btns { width: 100%; }
  .phead-btns form, .phead-btns button { flex: 1; }
}

/* an uploaded audit: the files brought in, and the words a finding came from */
.imp-row {
  display: flex; gap: 12px; align-items: flex-start; justify-content: space-between;
  padding: 9px 0; border-bottom: 1px solid var(--line);
}
.imp-row:last-child { border-bottom: 0; }
.imp-row form { margin: 0; }
.imp-name { font-size: 12.5px; color: var(--hi); overflow-wrap: anywhere; }
.ilist-split {
  padding: 10px 16px; border-bottom: 1px solid var(--line); background: var(--panel);
  font-family: var(--font-mono); font-size: 10.5px; color: var(--low); line-height: 1.6;
}
.doc-quote {
  margin: 0; padding: 10px 12px; border-left: 2px solid var(--note);
  background: var(--panel); border-radius: 0 5px 5px 0;
  font-size: 12.5px; color: var(--mid); line-height: 1.6; overflow-wrap: anywhere;
}

.tabs { display: flex; padding: 0 12px; border-bottom: 1px solid var(--line); overflow-x: auto; }
.tabs a {
  font-family: var(--font-mono); font-size: 11.5px; color: var(--low);
  padding: 10px 12px; border-bottom: 2px solid transparent; white-space: nowrap;
}
.tabs a.on { color: var(--hi); border-bottom-color: var(--note); }
.tabs a .n { color: var(--warn); margin-left: 6px; }

/* Two buttons now float bottom-right over the content — feedback in the corner
   and chat stacked above it — so every scrollable body reserves BOTH footprints
   and never hides its own last line. One number, in one place: change it here
   if either button moves. */
:root { --fab-clear: 152px; }
.tbody { padding: 24px 24px var(--fab-clear); }
/* The mobile-only sticky "have us fix it" bar under an open task
   (`.donow-sticky`, project.html's inline <style>, ~44px button + 20px own
   padding). Layout polish, 2026-09-08: "Worked through what's here?"
   (`.audit-mini`) sits right after the task panel in the page's own flow —
   on a phone, while the sticky bar is still pinned over the bottom of that
   panel, its top edge can already be on screen underneath it, clipped. */
:root { --donow-sticky-clear: 76px; }

/* tiles */
.tiles { display: flex; flex-wrap: wrap; gap: 14px; }
.tile { border: 1px solid var(--line); border-radius: 7px; padding: 14px; min-width: 168px; flex: 1 1 168px; background: var(--panel); }
.tile .k { font-family: var(--font-mono); font-size: 10px; color: var(--low); letter-spacing: .06em; text-transform: uppercase; }
.tile .v { font-family: var(--font-mono); font-size: 22px; margin-top: 6px; }
.tile .foot { display: flex; gap: 8px; margin-top: 4px; align-items: baseline; }
.tile .d { font-family: var(--font-mono); font-size: 11px; }
.tile .src { font-family: var(--font-mono); font-size: 10px; color: var(--low); }

.panel { border: 1px solid var(--line); border-radius: 8px; padding: 16px; background: var(--panel); }
.panel-row { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 14px; }
.panel-row .panel { flex: 1 1 300px; min-width: 0; }

/* ---- Home's hero + rail (redesign brief D4, Visitors/Profound) ----
   Phase 11, A-2: the score card itself is gone (Harsha's clip, 17 Sep) and
   with it every rule that dressed it — `.home-grid`, `.home-score-row`,
   `.home-score-n`, `.home-score-word`, `.home-drivers`, `.home-legend`,
   `.home-calc`. The score, the priority counts and the verdict live in the
   page header now (`.phead .nums`), the same strip every other tab uses.
   What is left of the hero is `.home-next`, styled further down the file. */
.home-rail { border: 1px solid var(--line); border-radius: 8px; padding: 14px; background: var(--panel); align-self: start; }
.home-rail-credits { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); }

/* ---- overview without Search Console (B1, 2026-09-03) ---- */
.ov-pricards { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 14px; }
/* The same high/medium/low counts as the page header's own tiles, a third
   time as the sentence just above this one spells them out again — on a
   narrow screen that is the number 1/16/13 three times in one scroll (F4,
   2026-09-03). The header still carries the tiles and the sentence still
   carries the "why" in words; this is the one that's pure repetition. */
@media (max-width: 900px) { .ov-pricards { display: none; } }
.ov-pricard {
  flex: 1 1 168px; min-width: 140px; border: 1px solid var(--line); border-radius: 7px;
  padding: 14px; background: var(--panel); text-decoration: none; transition: border-color .15s;
}
.ov-pricard:hover { border-color: var(--mid); }
.ov-pri-n { font-family: var(--font-mono); font-size: 26px; }
.ov-pri-l { font-size: 12px; color: var(--mid); margin-top: 4px; text-transform: capitalize; }
/* A ghost of the real chart — static bars, never real data, with the Connect
   CTA centred over it. Never blur real numbers; there are none to blur yet. */
.ov-ghost-wrap { position: relative; overflow: hidden; }
.ov-ghost-chart {
  display: flex; align-items: flex-end; gap: 5px; height: 160px; margin-top: 10px;
  opacity: .32; filter: saturate(0);
}
.ov-ghost-chart span { flex: 1 1 auto; background: var(--raise); border-radius: 3px 3px 0 0; min-width: 6px; }
.ov-ghost-cta {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 10px; text-align: center; padding: 20px;
  background: linear-gradient(180deg, transparent 0%, var(--panel) 60%);
}
.ov-ghost-cta p { margin: 0; max-width: 380px; color: var(--hi); font-size: 13px; }

/* Reports empty state, no Search Console (B3, 2026-09-03) — a skeleton chart
   beside a real example answered from rank snapshots, never a bare promise. */
.skel-bars { display: flex; align-items: flex-end; gap: 6px; height: 90px; margin-top: 8px; }
.skel-bars span {
  flex: 1 1 auto; min-width: 6px; border-radius: 3px 3px 0 0;
  background: var(--soft);
  animation: skel-pulse 1.8s ease-in-out infinite;
}
.skel-bars span:nth-child(even) { animation-delay: .3s; }
@keyframes skel-pulse { 0%, 100% { opacity: .5; } 50% { opacity: .9; } }

/* chart */
.chart-wrap { position: relative; }
.chart-tip {
  position: absolute; pointer-events: none; display: none; z-index: 5;
  background: var(--panel2); border: 1px solid var(--line); border-radius: 7px;
  padding: 8px 10px; font-size: 12px; min-width: 160px; box-shadow: var(--shadow);
}
.chart-tip .d { font-family: var(--font-mono); font-weight: 500; margin-bottom: 4px; font-size: 11px; }
.chart-tip .r { display: flex; align-items: center; gap: 6px; color: var(--mid); font-family: var(--font-mono); font-size: 11px; }
.chart-tip .sw { width: 8px; height: 8px; border-radius: 2px; }
.legend { display: flex; gap: 16px; flex-wrap: wrap; font-family: var(--font-mono); font-size: 10.5px; color: var(--mid); margin-bottom: 8px; }
.legend .sw { width: 10px; height: 10px; border-radius: 2px; display: inline-block; margin-right: 6px; vertical-align: -1px; }
/* Two single-axis charts stacked, sharing one x-axis — never a dual-axis
   chart (B1, 2026-09-03). A hairline between them stands in for the axis
   they share; only the bottom one prints month labels. */
.stack-row + .stack-row { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 4px; }
.range { display: flex; gap: 4px; margin-left: auto; }
.range a { font-family: var(--font-mono); font-size: 10.5px; color: var(--low); padding: 3px 8px; border-radius: 4px; border: 1px solid transparent; }
.range a.on { color: var(--hi); border-color: var(--line); background: var(--raise); }

/* date range — one control per project page, sitting under the tabs so the
   window is the first thing you see and the last thing you can blame a number on */
.drange {
  position: relative; display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding: 10px 22px; border-bottom: 1px solid var(--line); background: var(--panel);
}
.drange-lab {
  font-family: var(--font-mono); font-size: 10px; color: var(--low);
  letter-spacing: .06em; text-transform: uppercase;
}
.drange .range { margin-left: 0; }
.drange-when {
  margin-left: auto; font-family: var(--font-mono); font-size: 10.5px; color: var(--low);
}
.rng-custom {
  font-family: var(--font-mono); font-size: 10.5px; color: var(--low);
  background: none; border: 1px solid transparent; border-radius: 4px;
  padding: 3px 8px; cursor: pointer;
}
.rng-custom:hover { color: var(--mid); }
.rng-custom.on { color: var(--hi); border-color: var(--line); background: var(--raise); }
.rng-custom .caret { color: var(--low); margin-left: 2px; }
.rng-pop {
  position: absolute; z-index: 40; top: calc(100% - 4px); left: 22px;
  display: flex; align-items: flex-end; gap: 10px;
  background: var(--panel2); border: 1px solid var(--line); border-radius: 7px;
  padding: 12px; box-shadow: var(--shadow);
}
.rng-pop[hidden] { display: none; }
.rng-pop label {
  display: flex; flex-direction: column; gap: 5px;
  font-family: var(--font-mono); font-size: 10px; color: var(--low);
  letter-spacing: .06em; text-transform: uppercase;
}
.rng-pop input[type="date"] {
  color-scheme: light;
  font-family: var(--font); font-size: 12px; color: var(--hi);
  background: var(--bg); border: 1px solid var(--line); border-radius: 5px; padding: 7px 8px;
}
.rng-pop input[type="date"]:focus { outline: 2px solid var(--note); outline-offset: 1px; }
.chart-win {
  margin-left: auto; font-family: var(--font-mono); font-size: 10.5px; color: var(--low);
}
@media (max-width: 760px) {
  .drange { padding: 10px 14px; }
  .drange-when { margin-left: 0; width: 100%; }
  .rng-pop { left: 14px; right: 14px; flex-wrap: wrap; }
}

/* tables */
table.flat { width: 100%; border-collapse: collapse; }
table.flat th {
  text-align: left; font-family: var(--font-mono); font-size: 10px; color: var(--low);
  letter-spacing: .06em; text-transform: uppercase; font-weight: 400;
  padding: 6px 8px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
table.flat td { font-size: 12.5px; padding: 8px; border-bottom: 1px solid var(--soft); vertical-align: top; }
table.flat td.mono, table.flat .mono { font-family: var(--font-mono); font-size: 11.5px; }
/* R3: a ledger row for a feature that is not here yet ("Auto top-up: coming
   soon") — greyed with --low (still 4.65:1 on the darkest surface it sits
   on, so it reads, just quieter) rather than faded with opacity, which
   would drop it under the contrast floor. */
table.flat tr.row-soon td { color: var(--low); }
/* A table wider than the screen is fine; a table wider than the screen with
   nothing saying so is not. On a phone the right-hand columns were simply
   invisible and read as missing data, so the edge fades and the panel says
   which way to swipe. */
.scrollx { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.scrollx-hint { display: none; font-size: 11px; color: var(--low); margin: 0 0 6px; }
@media (max-width: 760px) {
  .scrollx { position: relative;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent); }
  .scrollx-hint { display: block; }
}

/* issues two-pane */
.two-pane { display: flex; min-height: 540px; }
.ilist { flex: 1; min-width: 0; }
.irow {
  display: block; width: 100%; text-align: left; padding: 14px 16px;
  background: transparent; border: none; border-bottom: 1px solid var(--soft);
  border-left: 2px solid transparent; color: var(--hi);
}
.irow.on { background: var(--panel2); border-left-color: var(--note); }
.irow:hover { background: var(--panel); }
.irow { transition: background var(--motion); }

/* D5, Tasks selection (redesign brief) — a checkbox beside the row, OUTSIDE
   the `<a>` (a checkbox nested in an anchor either fires both on one click or
   needs JS to fight the navigation; a sibling avoids both). */
.irow-line { display: flex; align-items: stretch; }
.irow-line .irow { flex: 1; border-left: 0; }
.irow-line.sel-row { border-left: 2px solid transparent; }
.tsel { flex-shrink: 0; width: 34px; align-self: center; cursor: pointer; accent-color: var(--note); }

/* the floating bar (mobile + desktop, Linear/ClickUp) and the desktop-only
   sticky summary (Fresha) — one shared "Do these", never a duplicate charge.

   C2 (fix round, checker 2026-09-09): `position: sticky` needs a scrolling
   ANCESTOR to stick within — `.ipane` deliberately has none (it uses the
   page's own scroll, not a boxed pane), so `sticky` never tracked the
   viewport at all, only wherever the list happened to end in the DOM; a
   long scroll (verified at scroll 900) left it behind. `position: fixed`
   on desktop too, same as mobile always had, tracks the viewport by
   definition — JS below (report.js) centres it over the LIST column
   specifically (not the whole viewport), since a fixed element has no
   notion of "its own column" the way a sticky one at least inherits from
   its flow position. */
.tselbar {
  position: fixed; bottom: 14px; left: 50%; transform: translateX(-50%);
  z-index: 6; display: flex;
  align-items: center; gap: 14px; width: fit-content; max-width: calc(100% - 32px);
  background: var(--panel2); border: 1px solid var(--line); border-radius: 999px;
  padding: 8px 8px 8px 18px; font-size: 12.5px; color: var(--hi);
  box-shadow: var(--shadow);
}
/* C1 (fix round, checker, TWO rounds): the original bug tracked `.ipane`'s
   own right edge via JS — with the detail panel's column beside it,
   `.ipane` is only the LEFT HALF of the content width, so "flush with the
   pane's right edge" landed around the horizontal MIDDLE of the page
   (checker: x≈599 at 1440). The first fix (a hardcoded `top: 78px; right:
   16px`, no JS) traded that bug for two more: `.phead`'s real height
   varies with the verdict sentence's own length (it can wrap to a second
   line), so a fixed 78px did not reliably clear it — and worse, at that
   position it sat directly over the detail panel's own primary-action
   cards the moment a task was selected, which no fixed `top` guess can
   avoid on its own, since the panel's real content occupies exactly the
   space a floating box would sit in.
   The actual fix (project.html's own `placeSide()`) measures `.rpane` —
   the RIGHT column itself, not `.ipane` — for both `top` and `right`, and
   reserves that same height as `.rpane`'s own `padding-top` so its real
   content is pushed down to start below `.tselside` rather than under it.
   `top`/`right` below are only the pre-JS fallback (a plausible value for
   the instant before the page's own script runs); the values that matter
   are computed at runtime. `position: fixed` (not `sticky`, which needs a
   scrolling ancestor `.ipane` deliberately has none of) is what survives
   the list's own long scroll — verified at scroll 0 and scroll 900. */
.tselside {
  display: none; position: fixed; top: 78px; right: 16px; width: 200px;
  padding: 14px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--panel2); z-index: 9;
  box-shadow: var(--shadow);
}
.tselside-row { display: flex; justify-content: space-between; font-size: 12.5px;
  color: var(--mid); padding: 4px 0; }
.tselside-row b { color: var(--hi); font-family: var(--font-mono); }
@media (min-width: 901px) { .tselside:not([hidden]) { display: block; } }
/* C3 (fix round, checker): `.tselbar{display:flex}` is an AUTHOR rule, so it
   beats the UA stylesheet's `[hidden]{display:none}` regardless of which
   one appears later in this file — origin order (UA < author), not source
   order, decides that fight, and author always wins. The result was "0
   selected · 0 credits · Do these" showing on every load. A same-origin
   rule with a stronger selector (class + attribute) is what actually
   overrides it. */
.tselbar[hidden], .tselside[hidden] { display: none; }
@media (max-width: 900px) {
  .tselbar { left: 12px; right: 12px; bottom: 70px; transform: none;
    width: auto; justify-content: space-between; z-index: 15; }
  /* Keyboard hints are a desktop affordance (Plain) — nothing on a touch
     device reads ↑ ↓ Esc as an instruction. */
  .panel-kbd-hint { display: none; }
}
/* No wrap on this row: with flex-wrap the dot and a long title are two flex
   items, so the title jumped to its own line and left the dot stranded above
   it. Nowrap keeps them side by side and lets the text wrap inside the title
   box; the dot is nudged down to sit on the first line rather than centred
   against a two-line block. */
.irow .t1 { display: flex; align-items: flex-start; gap: 8px; }
.irow .t1 .sevdot { margin-top: 5px; }
.irow .title { font-size: var(--fs-3); font-weight: 500; min-width: 0; }
.irow .t2 { display: flex; align-items: center; gap: 8px; margin-top: 8px; padding-left: 14px; flex-wrap: wrap; font-family: var(--font-mono); font-size: var(--fs-1); color: var(--low); }
/* Why a finding closed, in prose, on the row. Two lines at most in the list —
   the panel carries the whole sentence — because the point is that the reason
   is impossible to miss, not that the list becomes a document. */
.irow .t3 {
  margin-top: 8px; padding-left: 14px; font-size: var(--fs-2); line-height: 1.5;
  color: var(--mid);
}
/* the free tier's locked rows (C, 2026-09-04) — an Ahrefs-style fade to one
   CTA, not a wall: the rows themselves are real content (priority, price),
   only the title is a placeholder, so the blur is decoration on top of an
   honest row rather than a disguise over a hidden one.

   L5 (checker, 2026-09-04): the placeholder used to be a grey box —
   transparent text on a solid background — which reads as "still loading",
   not "locked on purpose". It is now a real sentence, the ONLY text this
   node ever carries, with a CSS blur filter doing the obscuring instead of
   a fake-content trick — the same visual language a paywalled article uses,
   which reads as deliberate. */
.tlist-locked { position: relative; margin-top: -1px; }
.irow-locked { display: block; width: 100%; padding: 12px 16px; border-bottom: 1px solid var(--soft); cursor: default; }
.irow-locked .locked-blur {
  display: inline-block; font-size: 13.5px; font-weight: 500; color: var(--hi);
  filter: blur(4px); user-select: none; pointer-events: none;
}
/* The compact "unlock" link riding beside the first locked row's title (L5)
   — the way out sits next to the first lock a reader hits, not a scroll
   away at the bottom of the fade. */
.irow-locked .irow-unlock-inline {
  margin-left: 10px; font-size: 12px; white-space: nowrap; pointer-events: auto;
}
.tlist-locked::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 90px; pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--bg) 85%);
}
.tlist-locked-cta { position: relative; display: flex; justify-content: center; padding: 14px 0 4px; }
@media (max-width: 1000px) {
  /* The unlock CTA used to sit flush against the mobile sticky "have us do
     this" bar the moment a locked board was short enough for both to land
     in one screenshot — two filled buttons read as a confusing pair
     (checker, 390px, 2026-09-04). Real air between the end of the list and
     the start of the task pane, so the two are never mistaken for one. */
  .tlist-locked { margin-bottom: 32px; }
}
.idetail { width: 400px; flex-shrink: 0; border-left: 1px solid var(--line); display: flex; flex-direction: column; }
.idetail-h { padding: 14px 16px; border-bottom: 1px solid var(--line); }
/* Fix round (checker M4): the old `--fab-clear` (152px) bottom padding is
   retired here and on every other `.idetail-b` / `.idetail` rule below:
   `.app-scroll` alone reserves the bottom for the nav, the dock and the
   inset (H4-03), so an opened task ends at its Collapse control. */
.idetail-b { padding: 16px 16px 16px; overflow: auto; flex: 1; }
/* action bar sticks to the bottom of the viewport while the detail pane is on screen */
.idetail-f {
  /* the right padding is clearance for the floating feedback button, which is
     fixed to the viewport's bottom-right — exactly where this bar ends. It was
     sitting on top of the channel name. */
  padding: 12px 76px 12px 16px; border-top: 1px solid var(--line);
  display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
  flex-shrink: 0; background: var(--bg);
}
/* Not sticky. It was pinned to the bottom of the viewport, but this panel is
   not its own scroll container — the PAGE scrolls — so the bar sat wherever the
   reader happened to be and laid a band across the card behind it, hiding the
   third option of a decision that only had three. Measured: panel scrollHeight
   equals clientHeight, so there was never anything for it to stick to. The
   actions it used to pin are at the TOP of the panel now anyway; all this
   carries is a status line, which belongs at the end of what it describes. */
/* a channel name is one unbreakable token and the panel is 400px: without this
   #a-long-channel-name is clipped at the panel edge mid-word */
.idetail-f > * { min-width: 0; overflow-wrap: anywhere; }
/* the two things you do with an issue, at the top where you read it */
.idetail-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.idetail-acts .btn-primary[disabled] { background: var(--raise); color: var(--mid); cursor: default; }
/* The `hidden` attribute on this block (set only when project.html's
   donow-side sticky bar is also on screen) does nothing here — the `display:
   flex` two lines up is an author rule and always beats the browser's own
   [hidden]{display:none}. At the 900px width where donow-side's sticky bar
   takes over as the one purchase CTA, that meant the SAME "have us do this"
   button twice, 52px apart (F2, 2026-09-03). "create brief" is not on the
   sticky bar, so it stays here as the secondary action. */
@media (max-width: 900px) {
  .idetail-acts .btn-primary, .idetail-acts a.btn-primary { display: none; }
}

/* modal */
.modal { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; padding: 24px; }
.modal[hidden] { display: none; }
.modal-back { position: absolute; inset: 0; background: var(--scrim); }
.modal-card {
  position: relative; z-index: 1; width: 100%; max-width: 520px; max-height: 86vh;
  display: flex; flex-direction: column; text-align: left;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: var(--shadow);
  animation: fade 150ms ease both;
}
.modal-card.wide { max-width: 720px; }
.modal-h { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--line); }
.modal-h h3 { margin: 0; font-size: var(--fs-4); font-weight: 600; flex: 1; }
.modal-x { background: transparent; border: 0; color: var(--low); font-size: 20px; line-height: 1; padding: 0 4px; }
.modal-x:hover { color: var(--hi); }
.modal-b { padding: 16px; overflow: auto; flex: 1; }
.modal-f { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 12px 16px; border-top: 1px solid var(--line); }
.modal-f .note-t { flex: 1; min-width: 160px; }
.brief-text {
  margin: 0; padding: 12px; max-height: 52vh; overflow: auto;
  background: var(--bg); border: 1px solid var(--line); border-radius: 6px;
  font-family: var(--font-mono); font-size: 11.5px; line-height: 1.65;
  color: var(--hi); white-space: pre-wrap; overflow-wrap: anywhere;
}
body.modal-open { overflow: hidden; }
@media (max-width: 620px) {
  .modal { padding: 12px; }
  .modal-f { flex-direction: column; align-items: stretch; }
  .modal-f button { width: 100%; }
}

/* readable evidence — example URLs first, then measured facts */
.ev-head { font-size: 13px; color: var(--hi); font-weight: 600; margin: 0 0 8px; }
.ev-sub { font-size: 12px; color: var(--low); margin: 14px 0 6px; }
.ev-prose { font-size: var(--fs-2); color: var(--mid); line-height: 1.6; margin: 0 0 16px; }
.ev-pages { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.ev-pages li { background: var(--soft); border: 1px solid var(--line); border-radius: 5px; padding: 6px 8px; }
.ev-pages a { font-family: var(--font-mono); font-size: 11.5px; overflow-wrap: anywhere; }
.ev-pages a:hover { text-decoration: underline; }
.ev-kw { display: block; font-size: 11px; color: var(--low); margin-top: 2px; }
/* opened from a link, but retired: the panel must not read as current work */
.ev-retired {
  border: 1px solid var(--line); border-left: 3px solid var(--warn);
  background: var(--soft); border-radius: 5px;
  padding: 10px 12px; margin: 0 0 16px;
}
.ev-retired p { font-size: 12.5px; color: var(--mid); line-height: 1.6; margin: 0; }
.ev-retired p + p { margin-top: 6px; }
/* The account of a closure. Same shape as the retired notice — it answers the
   same question — in the good colour when we changed the page and the warning
   colour when we did not. */
.ev-closed {
  border: 1px solid var(--line); border-left: 3px solid var(--good);
  background: var(--soft); border-radius: 5px;
  padding: 10px 12px; margin: 0 0 16px;
}
.ev-closed p { font-size: 12.5px; color: var(--mid); line-height: 1.6; margin: 0; }
.ev-closed p + p { margin-top: 6px; }
.ev-retired b { color: var(--hi); font-weight: 600; }
/* our own recommendation, found in place and filed as a fault. Green edge, not
   amber: nothing here is wrong, and the panel should read that way at a glance */
.ev-intended {
  border: 1px solid var(--line); border-left: 3px solid var(--good);
  background: var(--soft); border-radius: 5px;
  padding: 10px 12px; margin: 0 0 16px;
}
.ev-intended p { font-size: 12.5px; color: var(--mid); line-height: 1.6; margin: 0 0 8px; }
.ev-intended b { color: var(--hi); font-weight: 600; }
/* the closed drawer under the list. Quiet by default — it is not work */
.ilist-intended { border-top: 1px solid var(--line); background: var(--panel); }
.ilist-intended > summary {
  padding: 12px 16px; cursor: pointer; list-style: none;
  font-family: var(--font-mono); font-size: 10.5px;
  color: var(--low); letter-spacing: .02em;
}
.ilist-intended > summary::-webkit-details-marker { display: none; }
.ilist-intended > summary::before { content: "▸ "; color: var(--good); }
.ilist-intended[open] > summary::before { content: "▾ "; }
.ilist-intended > summary:hover { color: var(--mid); }
/* the after: the fix drawn, not a photograph of what is there today */
.ev-mock { margin-top: 10px; }
.ev-mock .mock-frame { display: block; width: 100%; border: 1px solid var(--line); border-radius: 6px; background: #fff; }
/* Layout polish, 2026-09-08: `.note-t` is a <span> — an inline element's
   top margin does not push a new line, so the hint used to just flow on
   beside the button and wrap mid-sentence around it in a narrow rail.
   `display: block` puts it on its own line, under the button, the way the
   6px top margin already implied it should read. */
.ev-mock .note-t { display: block; margin: 6px 0 0; }
.ev-mock button { margin-right: 8px; }
/* no drawing, but never a blank box: the reason, shaped like an answer */
.mock-note {
  margin-top: 8px; padding: 11px 13px; border: 1px solid var(--line);
  border-left: 3px solid var(--low); border-radius: 6px; background: var(--panel2);
  font-size: 12px; line-height: 1.6; color: var(--mid);
}
.mock-note p { margin: 0; }
.mock-note .mock-note-h { color: var(--hi); font-weight: 600; margin-bottom: 4px; }
.mock-note .mock-note-f { color: var(--low); margin-top: 7px; }
/* a finding contradicted by the live page is a result, not an error */
.mock-note-stale { border-left-color: var(--warn); }
.mock-note-stale .mock-note-h { color: var(--warn); }
.btn-sm { padding: 5px 9px; font-size: var(--fs-1); }
.ev-suggest { font-size: 11.5px; color: var(--mid); background: var(--soft); border: 1px dashed var(--line); border-radius: 5px; padding: 6px 8px; margin: 0; overflow-wrap: anywhere; }
.ev-facts { width: 100%; border-collapse: collapse; margin-bottom: 16px; }
.ev-facts th { text-align: left; font-weight: 400; color: var(--low); font-size: 12px; padding: 4px 8px 4px 0; vertical-align: top; white-space: nowrap; }
.ev-facts td { text-align: right; font-family: var(--font-mono); font-size: 11.5px; color: var(--hi); padding: 4px 0; overflow-wrap: anywhere; }
/* A long value pushed hard right reads as unrelated to its label. Below the
   width where that starts happening, stack the pair so it reads as one fact. */
@media (max-width: 1100px) {
  .ev-facts, .ev-facts tbody, .ev-facts tr, .ev-facts th, .ev-facts td { display: block; }
  .ev-facts tr { margin-bottom: 8px; }
  .ev-facts th { white-space: normal; padding: 0; }
  .ev-facts td { text-align: left; padding: 1px 0 0; }
}
@media (max-width: 1100px) {
  .two-pane { flex-direction: column; }
  .idetail { width: auto; border-left: 0; border-top: 1px solid var(--line); }
  .rail { display: none; }
  /* Stacked, the detail panel comes AFTER the list, so the floating button sits
     over the panel's last line, not the list's. Clearance belongs on whatever
     is last: putting it on the list left a screen-height dead gap between the
     two panes AND still let the button cover the panel footer. */
  .ilist { padding-bottom: 0; }
  .idetail { padding-bottom: 0; }   /* fix round M4: .app-scroll carries the clearance */
  .two-pane { min-height: 0; }
}

/* changes — attribution view */
.frame { display: flex; gap: 22px; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; margin-bottom: 14px; }
.frame-l { flex: 1 1 420px; min-width: 0; }
.frame-h { font-size: 17px; font-weight: 700; letter-spacing: -.01em; margin: 0 0 5px; }
.frame-p { font-size: 13px; color: var(--mid); line-height: 1.65; margin: 0; max-width: 640px; }
.frame-p b { color: var(--hi); font-weight: 500; }
.frame-r { display: flex; flex-direction: column; gap: 8px; align-items: flex-end; }
.frame-ctl { display: flex; align-items: center; gap: 9px; }
.frame-lab { font-family: var(--font-mono); font-size: 10px; color: var(--low); letter-spacing: .06em; text-transform: uppercase; }
.frame-ctl .range { margin-left: 0; }

.sumline { display: flex; gap: 14px; flex-wrap: wrap; align-items: baseline; margin-bottom: 12px;
  font-family: var(--font-mono); font-size: 11.5px; color: var(--mid); }
.sumline b { color: var(--hi); font-size: 13px; }

/* Results summary sentence (redesign brief P5a) — one plain-English line at
   the top of every Results section, built server-side from real data only. */
.results-summary { font-size: var(--fs-4); color: var(--hi); margin: 0 0 16px; line-height: 1.5; }

/* one moved page */
.mv { border-top: 1px solid var(--soft); }
.mv:first-of-type { border-top: 0; }
.mv[open] { background: var(--panel2); }
.mv-h { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 12px 16px; cursor: pointer; list-style: none; }
.mv-h::-webkit-details-marker { display: none; }
.mv-h:hover { background: var(--panel2); }
.mv-d { font-family: var(--font-mono); font-size: 17px; font-weight: 500;
  min-width: 78px; text-align: right; }
.mv-mu { font-family: var(--font-mono); font-size: 10px; color: var(--low);
  margin-left: -7px; }
.mv-u { font-size: 13.5px; color: var(--hi); flex: 1 1 260px; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mv-n { font-family: var(--font-mono); font-size: 10.5px; color: var(--low); }
/* The cause, on its own line under the numbers: it is a sentence, not a chip,
   and putting it inline pushed the verdict off the row on a narrow screen. */
.mv-c { flex-basis: 100%; margin-left: 90px; font-size: 11.5px; color: var(--mid);
  overflow-wrap: anywhere; }
.mv-tag { font-family: var(--font-mono); font-size: 10px; color: var(--low);
  border: 1px solid var(--line); border-radius: 3px; padding: 2px 7px; white-space: nowrap; }
.mv-tag.on { color: var(--note); border-color: var(--note); }
.mv-b { padding: 2px 16px 14px 94px; }
@media (max-width: 760px) {
  .mv-b { padding-left: 16px; }
  .mv-d { min-width: 0; text-align: left; }
  /* Show the whole path on a phone. Ellipsis-truncating a URL hides the one
     part that identifies the page, and the tail ran under the floating button;
     wrapping costs a line and keeps the page name readable. */
  .mv-u { white-space: normal; overflow: visible; overflow-wrap: anywhere; }
  /* keep the last rows clear of the fixed feedback button */
  .mv:last-of-type .mv-b { padding-bottom: 26px; }
}

/* one change behind that movement */
.cz { border-left: 2px solid var(--line); padding: 8px 0 8px 12px; margin-top: 10px; }
.cz:first-child { margin-top: 4px; }
.cz.art { opacity: .72; border-left-style: dashed; }
.cz-1 { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.cz-when { font-family: var(--font-mono); font-size: 11px; color: var(--mid); }
.cz-lag { font-family: var(--font-mono); font-size: 10.5px; color: var(--low); }
/* the whole point of this view — a change that sits outside the measured window */
.cz-lag.out { color: var(--warn); border: 1px solid var(--warn); border-radius: 3px;
  padding: 2px 7px; background: var(--warn-bg); }
.cz-cav { font-size: 12px; color: var(--warn); line-height: 1.6; margin: 7px 0 0; max-width: 660px; }
.cz-eff { font-family: var(--font-mono); font-size: 11px; color: var(--mid);
  margin-top: 7px; line-height: 1.65; }
.cz-early { color: var(--warn); margin-left: 6px; }
.cz-diff { font-size: 12px; color: var(--low); margin-top: 6px; line-height: 1.6;
  max-width: 780px; overflow-wrap: anywhere; }
.cz-diff .b { text-decoration: line-through; opacity: .7; }
/* what the change actually was, in words — sits under the type label on the
   changes tab and under it again in the overview table, hence display:block */
.cz-desc { display: block; font-size: 12.5px; color: var(--mid); line-height: 1.6;
  margin: 6px 0 0; max-width: 720px; overflow-wrap: anywhere; }
td .cz-desc { font-size: 11.5px; margin-top: 3px; white-space: normal; }

/* where a change is known from. Both chips on one edit means we recorded doing
   it and the crawler then saw it — one change, two witnesses, not two changes. */
.cz-1 .src { font-family: var(--font-mono); font-size: 10px; letter-spacing: .02em;
  border: 1px solid var(--line); border-radius: 3px; padding: 2px 7px; color: var(--low);
  white-space: nowrap; }
.cz-1 .src-log { color: var(--note); border-color: var(--note); }
/* the site's own account of an edit: the strongest witness we have, so it reads
   as the strongest chip. Exact minute, real before, real after. */
.cz-1 .src-site { color: var(--note); border-color: var(--note); font-weight: 500; }

/* the keyword half of a change's impact: position on the term the page is
   actually worth something on, not just the page's own totals */
.kwi { display: flex; flex-wrap: wrap; align-items: baseline; gap: 5px 10px; margin-top: 7px;
  padding: 7px 10px; background: var(--panel2); border-radius: 5px; }
.kwi-l { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--low); }
.kwi-k { font-size: 13px; color: var(--hi); font-weight: 500; overflow-wrap: anywhere; }
.kwi-p, .kwi-n { font-family: var(--font-mono); font-size: 11px; color: var(--mid); }
.kwi-p b { color: var(--hi); font-weight: 500; }
.kwi-m { flex-basis: 100%; font-size: 11px; color: var(--low); line-height: 1.6; }
.kwi-w { font-size: 12px; color: var(--low); line-height: 1.6; overflow-wrap: anywhere; }
@media (max-width: 760px) {
  /* a long value stacks under its label rather than pushing the row sideways */
  .kwi { flex-direction: column; align-items: flex-start; }
}

.quiet { margin-top: 18px; }
.quiet > summary { font-family: var(--font-mono); font-size: 11.5px; color: var(--low);
  cursor: pointer; padding: 6px 0; }
.quiet > summary:hover { color: var(--mid); }

/* changes timeline */
.day { padding: 12px 0 6px; border-top: 1px solid var(--line); }
.day:first-child { border-top: 0; }
.dayhead { font-family: var(--font-mono); font-size: 12px; color: var(--hi); }
.dayhead .n { color: var(--low); font-size: 10.5px; }
.chg { display: flex; gap: 10px; padding: 7px 0 7px 8px; align-items: baseline; flex-wrap: wrap; }
.chg .url { font-family: var(--font-mono); font-size: 11.5px; color: var(--mid); max-width: 420px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chg .diff { font-size: 12px; color: var(--low); width: 100%; padding-left: 2px; }
.chg .diff .b { text-decoration: line-through; opacity: .7; }
.chg .eff { font-family: var(--font-mono); font-size: 11px; white-space: nowrap; }

/* activity */
.logrow { display: flex; gap: 12px; padding: 10px 16px; border-top: 1px solid var(--soft); flex-wrap: wrap; }
.logrow:first-child { border-top: 0; }
.logrow .t { font-family: var(--font-mono); font-size: 11px; color: var(--low); width: 138px; flex-shrink: 0; }
.logrow .actor { font-family: var(--font-mono); font-size: 10px; border: 1px solid var(--line); border-radius: 3px; padding: 1px 6px; height: 18px; white-space: nowrap; }
.logrow .txt { font-family: var(--font-mono); font-size: 11.5px; color: var(--mid); flex: 1; min-width: 200px; }

/* connections */
/* C13 (fix round): the Google group heading above the gsc/ga4 rows. */
.conn-group {
  display: flex; align-items: center; gap: 7px; padding: 12px 16px 4px;
  font-family: var(--font-mono); font-size: 10px; color: var(--low);
  letter-spacing: .1em; text-transform: uppercase;
}
.conn-group .conn-logo { margin-right: 0; }
.conn-group + .connrow { border-top: 0; }
.connrow { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 18px; border-top: 1px solid var(--soft); flex-wrap: wrap; }
/* Layout polish, 2026-09-08: the client connections panel now spans the
   full tab width like every other tab's panel does — this is the piece
   that keeps the ORIGINAL B3 fix (button close to its row's text) without
   putting that cap back on the panel around it. `space-between` alone, at
   1440px width, would still stretch a two-item row (paragraph + one
   button) the full distance; capped here instead, left-aligned in the wide
   panel, the same way the rest of the row's own text already is. */
.connrow-narrow { justify-content: flex-start; max-width: 640px; }
.connrow-narrow > *:last-child { margin-left: auto; }
/* Connection marks (B3, 2026-09-03) — plain line icons, not brand marks. */
.conn-logo { width: 15px; height: 15px; margin-right: 7px; vertical-align: -3px; color: var(--mid); flex-shrink: 0; }
.conn-value { color: var(--mid) !important; }
.connrow:first-child { border-top: 0; }
.connrow .nm { font-size: var(--fs-3); font-weight: 500; display: flex; align-items: center; gap: 8px; }
/* Phase 5: the line under a connection is a sentence about what it does,
   so it is set in the body face at small size, not as a mono caption */
.connrow .dt { font-size: var(--fs-2); line-height: 1.5; color: var(--low); margin-top: 4px; padding-left: 14px; max-width: 520px; }
.connrow .right { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-family: var(--font-mono); font-size: var(--fs-1); color: var(--low); }
/* The row button has to read as a control. Quiet was tried first and failed:
   an outline-only button in --low sat next to a .chip that is also a 1px
   outline in mono at the same size, so it scanned as a third status label
   and the row looked like it had no button at all. A filled surface and a
   bright label are what separate "press me" from "read me" here. */
.connrow .conn-how {
  padding: 6px 11px; font-size: 11px; border-radius: 5px; white-space: nowrap;
  color: var(--hi); background: var(--raise); border: 1px solid var(--line);
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.connrow .conn-how:hover { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
/* A source that is not connected yet is the one with something to do, so it
   carries the accent rather than sharing the neutral "reference" treatment. */
.connrow .conn-how.todo { color: var(--note); border-color: var(--line); }
.connrow .conn-how.todo:hover { color: var(--accent-ink); }

/* "I don't have this — set it up for me." An admission, so it must not look
   like a lesser Connect: it is a sentence you press, sitting under the
   description it answers, rather than a second button competing with the one
   in the right-hand column. */
.setup-ask { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 8px 0 0; }
.btn-link {
  font: inherit; font-size: 12.5px; color: var(--note); background: none; border: 0;
  padding: 0; text-align: left; text-decoration: underline;
  text-underline-offset: 3px; text-decoration-color: var(--line);
}
.btn-link:hover { text-decoration-color: var(--mid); }
.btn-link:focus-visible { outline-offset: 3px; border-radius: 2px; }
.btn-link.low { color: var(--low); text-decoration-color: var(--line); font-size: 12px; }
.btn-link.low:hover { color: var(--mid); }
.setup-ask .note-t { max-width: 420px; }

/* Disconnect. Available, never inviting: it sits quiet until you go for it,
   then turns the colour of the thing it does. Same footprint as the Connect
   button it replaces so the row does not jump when a connection lands. */
.btn-off {
  font-family: var(--font-mono); font-size: 11px; white-space: nowrap;
  padding: 6px 11px; border-radius: 5px; cursor: pointer;
  color: var(--mid); background: transparent; border: 1px solid var(--line);
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.btn-off:hover { color: var(--panel); background: var(--crit); border-color: var(--crit); }
.btn-off:focus-visible { outline: 2px solid var(--crit); outline-offset: 2px; }

/* how-to-connect steps inside the connection dialogs */
ol.howto { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 10px; }
ol.howto li { font-size: 13px; color: var(--mid); line-height: 1.6; padding-left: 4px; }
ol.howto li::marker { color: var(--low); font-family: var(--font-mono); font-size: 11px; }

/* login */
.login-box { max-width: 360px; margin: 12vh auto; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 26px; }
.login-box h1 { margin: 0 0 4px; font-size: 22px; }
.login-box p { color: var(--mid); font-size: 13px; margin: 0 0 18px; }
.login-box form { display: grid; gap: 10px; }
.err { color: var(--crit); font-size: 13px; }
.note-t { color: var(--low); font-size: var(--fs-2); line-height: 1.6; }

/* rankings — the keyword filters, the focus ticks and the bar that appears
   with them. The bar only shows once something is ticked, so the tab reads
   the same as it always did until you start choosing. */
.kwhead { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
.kwhead .range { margin-left: auto; }
.range a .n { color: var(--note); margin-left: 5px; }
.kwbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--panel2); border: 1px solid var(--line); border-radius: 7px;
  padding: 9px 12px; margin-bottom: 10px;
}
.kwbar[hidden] { display: none; }
.kwbar .mono { font-family: var(--font-mono); font-size: 11px; color: var(--mid); margin-right: auto; }
/* Include / exclude, the way a rank tool does it. Sits between the pills and
   the table because that is the order it is used in: pick the group, narrow it,
   tick what is left, say what they are. */
.kwfilter {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: var(--panel2); border: 1px solid var(--line); border-radius: 7px;
  padding: 9px 12px; margin-bottom: 8px;
}
.kwf-l { font-family: var(--font-mono); font-size: 10.5px; color: var(--mid);
         letter-spacing: .04em; text-transform: uppercase; }
.kwf-i { font-family: var(--font-mono); font-size: 11.5px; color: var(--hi);
         background: var(--panel); border: 1px solid var(--line); border-radius: 5px;
         padding: 5px 8px; width: 190px; }
.kwf-i:focus { outline: none; border-color: var(--note); }
.kwf-i::placeholder { color: var(--low); }
.kwf-m { font-family: var(--font-mono); font-size: 11px; color: var(--hi);
         background: var(--panel); border: 1px solid var(--line); border-radius: 5px;
         padding: 5px 6px; }
.kwf-n { font-size: 10.5px; color: var(--mid); margin-left: auto; }
.kwf-n[hidden], .kwf-said[hidden], .kwf-none[hidden] { display: none; }
.kfhide { display: none !important; }
@media (max-width: 720px) {
  .kwfilter { gap: 6px; }
  .kwf-i { flex: 1 1 100%; width: auto; }
  .kwf-n { margin-left: 0; }
}
.kwtable .tickcol { width: 30px; padding-right: 0; }
.kwtable input[type="checkbox"] {
  width: 14px; height: 14px; accent-color: var(--note); cursor: pointer; margin: 0;
}
.star { color: var(--note); font-size: 11px; }

/* --- attribution: the verdict pill, the answer line, the per-page chart --- */
.vp { font-family: var(--font-mono); font-size: 10px; letter-spacing: .02em;
      padding: 3px 8px; border-radius: 3px; border: 1px solid var(--line);
      color: var(--low); white-space: nowrap; }
.vp-good { color: var(--good); border-color: var(--good); }
.vp-crit { color: var(--crit); border-color: var(--crit); }
.vp-warn { color: var(--warn); border-color: var(--warn); }
.vp-none, .vp-low { color: var(--low); border-color: var(--line); }
.vd { font-size: 13px; line-height: 1.65; color: var(--mid); margin: 2px 0 14px;
      max-width: 720px; border-left: 2px solid var(--line); padding-left: 12px; }
.vd-good { border-left-color: var(--good); color: var(--hi); }
.vd-crit { border-left-color: var(--crit); color: var(--hi); }
.vd-warn { border-left-color: var(--warn); }
.mv-chart { margin: 0 0 16px; max-width: 860px; }
.cz-num { display: inline-flex; align-items: center; justify-content: center;
          width: 17px; height: 17px; border-radius: 50%; background: var(--warn);
          color: var(--panel); font-family: var(--font);
          font-size: 9.5px; font-weight: 600; flex: none; }
.cz-num.art { background: var(--low); }
.warn-c { color: var(--warn); }

/* your call — a suggested fix that forks can't be handed over as it stands.
   Sits inside the evidence panel, directly under the fix text the options were
   cut from, and carries the two actions itself so there is never a live pair of
   buttons and a dead pair on the same screen. */
.decide {
  margin: 18px 0 4px; padding: 14px; border: 1px solid var(--line);
  border-left: 3px solid var(--note); border-radius: 8px; background: var(--panel2);
}
.decide .opt {
  display: flex; gap: 10px; align-items: flex-start; padding: 10px 12px; margin-bottom: 7px;
  border: 1px solid var(--line); border-radius: 6px; background: var(--bg); cursor: pointer;
}
.decide .opt:hover { border-color: var(--mid); }
.decide .opt:has(input:checked) { border-color: var(--mid); background: var(--soft); }
.decide .opt input { margin: 3px 0 0; accent-color: var(--note); flex-shrink: 0; padding: 0; }
.opt-b { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.opt-c { font-family: var(--font-mono); font-size: 10px; color: var(--mid);
  letter-spacing: .06em; text-transform: uppercase; }
.opt-l { font-size: 13px; color: var(--hi); font-weight: 500; }
.opt-t { font-size: 12px; color: var(--mid); line-height: 1.5; }
/* the recommended option — a view on the choice, never the choice itself, so it
   is marked and reasoned but never pre-ticked. */
.decide .opt-rec { border-color: var(--note); }
.rec-badge {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--note); border: 1px solid var(--note);
  border-radius: 3px; padding: 1px 5px; margin-left: 6px; vertical-align: 1px;
  font-weight: 400; white-space: nowrap;
}
.rec-basis {
  margin: 10px 0 2px; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 6px; background: var(--bg);
}
.rec-list {
  margin: 6px 0; padding-left: 16px; font-size: 12px; color: var(--mid);
  line-height: 1.6;
}
#decide-custom { display: none; margin: 2px 0 4px; }
#decide-custom.on { display: block; }
.decide-f { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 10px; }
.decide-f .btn-primary[disabled] { background: var(--raise); color: var(--mid); cursor: default; }
.decided {
  padding: 10px 12px; border: 1px solid var(--good); border-radius: 6px;
  background: var(--good-bg);
}
.decided-t { margin: 0; font-size: 13.5px; color: var(--hi); }
.decide-undo { margin-top: 8px; }
.btn-ghost.sm { font-size: var(--fs-1); padding: 4px 9px; }
.decide-acts {
  display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px;
  padding-top: 12px; border-top: 1px solid var(--line);
}
.decide-acts .btn-primary[disabled], .decide-acts .btn-ghost[disabled] {
  background: var(--raise); color: var(--low); border-color: var(--line); cursor: not-allowed;
}
.needs-call {
  font-family: var(--font-mono); font-size: 11px; color: var(--warn);
  border: 1px solid var(--warn); border-radius: 4px; padding: 6px 9px;
}

/* chat — floats bottom-right on every logged-in page. Sits ABOVE the feedback
   button, which owns the corner itself; stacking rather than sharing the spot
   is what keeps the two from covering each other. */
#chat-fab {
  position: fixed; right: 20px; bottom: 92px; z-index: 60;
  width: 46px; height: 46px; border-radius: 50%; line-height: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--accent-ink); border: none;
  box-shadow: var(--shadow);
}
#chat-fab:hover { filter: brightness(1.08); }
#chat-panel {
  position: fixed; right: 20px; bottom: 148px; z-index: 61;
  width: 380px; max-width: calc(100vw - 40px);
  height: 520px; max-height: calc(100vh - 190px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow);
}
#chat-panel[hidden] { display: none; }
#chat-head {
  display: flex; align-items: center; gap: 8px; padding: 11px 13px;
  border-bottom: 1px solid var(--line); font-size: 13px; font-weight: 600; color: var(--hi);
}
#chat-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#chat-close {
  background: none; border: 1px solid var(--line); color: var(--mid);
  border-radius: 6px; padding: 2px 8px; font-size: 13px;
}
#chat-msgs { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 9px; }
.cmsg { font-size: 12.8px; line-height: 1.55; padding: 9px 11px; border-radius: 10px; white-space: pre-wrap; word-break: break-word; }
.cmsg.them { background: var(--panel2); color: var(--hi); border-top-left-radius: 3px; }
.cmsg.me { background: var(--accent); color: var(--accent-ink); align-self: flex-end; border-top-right-radius: 3px; max-width: 88%; }
#chat-in { display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--line); }
#chat-in textarea { height: 40px; max-height: 120px; }
#chat-in button {
  font-family: var(--font); font-size: 11.5px; color: var(--accent-ink);
  background: var(--accent); border: none; border-radius: 6px; padding: 0 15px;
}
#chat-in button:disabled { background: var(--raise); color: var(--mid); }
@media (max-width: 620px) {
  #chat-panel { right: 12px; left: 12px; width: auto; bottom: 140px; max-height: calc(100vh - 180px); }
  #chat-fab { right: 14px; bottom: 84px; }
}

/* ---- Phase 13 (D4, D1): the roadmap's "Ask about your roadmap" bubble ----
   "I feel like this part the chat part should act like a chat bubble to the
   bottom right and then when people click on it it opens up the chat like a
   normal chat with you." A client's own tool — never `#chat-fab`'s
   read-anything assistant above (`_chat.html` stays staff-only) — so it
   owns the corner outright for a client: `#chat-fab` never renders there at
   all (base.html's `show_chat` gate). For staff BOTH exist, so this one
   sits to `#chat-fab`'s LEFT at the same height (`.ask-fab-staff`) rather
   than share or cover its spot; `#ask-fab.ask-fab-staff ~ #ask-panel`
   carries the panel to the same corner via the sibling combinator, since
   the two elements are siblings in `_ask_bubble.html`, in that order. */
#ask-fab {
  position: fixed; right: 20px; bottom: 20px; z-index: 59;
  height: 52px; min-width: 52px; padding: 0 18px; border-radius: 26px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--accent); color: var(--accent-ink); border: none;
  box-shadow: var(--shadow); line-height: 0;
}
#ask-fab:hover { filter: brightness(1.08); }
#ask-fab svg { flex: none; }
.ask-fab-label { line-height: 1; font-size: 13px; font-weight: 600; white-space: nowrap; }
/* Below 901px the label goes and the fab is a bare circle — a phone's
   44px+ hit target with no risk of the label pushing it wider than the
   corner it sits in (B-1: a text label ">= 901px"). */
@media (max-width: 900px) {
  #ask-fab { padding: 0; width: 52px; min-width: 52px; border-radius: 50%; }
  .ask-fab-label { display: none; }
}
/* Checker catch (screenshot review, 19 Sep): `.ask-fab-staff` alone (class,
   specificity 0,1,0) never beat the base `#ask-fab` rule above (ID,
   specificity 1,0,0) — the override silently never applied and staff got
   the CLIENT position, sitting the fab right on top of the feedback
   button's own corner. `#ask-fab.ask-fab-staff` (1,1,0) is what actually
   wins. */
#ask-fab.ask-fab-staff { right: 78px; bottom: 92px; }

#ask-panel {
  position: fixed; right: 20px; bottom: 84px; z-index: 61;
  width: 380px; max-width: calc(100vw - 32px);
  height: min(640px, calc(100vh - 120px));   /* 560 left the thread ~190px once five chips wrapped */
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow);
}
#ask-fab.ask-fab-staff ~ #ask-panel { right: 78px; bottom: 156px; }
#ask-panel[hidden] { display: none; }
/* Ben on Phase 13 (B13-3): folded to one sideways row, the third chip was cut
   mid-word ("Whic…") and nothing said there were more. On a desktop the chips
   wrap, every label whole: the thread now takes the rest of the panel and an
   answer opens on its top, so the room they cost no longer hides the answer,
   and a question already in the conversation is not offered again (B13-7).
   On a phone the row is a touch strip; it fades at the right edge so it reads
   as one. */
@media (max-width: 640px) {
  #ask-panel .ask-suggest {
    -webkit-mask-image: linear-gradient(to right, black calc(100% - 40px), transparent);
    mask-image: linear-gradient(to right, black calc(100% - 40px), transparent);
    padding-right: 40px;
  }
}
.ask-panel-head {
  display: flex; align-items: center; gap: 8px; padding: 12px 14px;
  border-bottom: 1px solid var(--line); font-size: 13.5px; font-weight: 600; color: var(--hi);
  flex: none;
}
.ask-panel-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#ask-panel-close {
  background: none; border: 1px solid var(--line); color: var(--mid);
  border-radius: 6px; padding: 2px 9px; font-size: 16px; line-height: 1;
  min-width: 28px; min-height: 28px;
}
#ask-panel-close:hover { color: var(--hi); }
.ask-panel-body {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  overflow: hidden; padding: 0 14px 14px;
}
.ask-greet { font-size: 12.5px; color: var(--mid); line-height: 1.5; margin: 12px 0 8px; }
/* `.ask-chat` itself grows to fill the panel body — its own rule, in the
   Phase 12 section below, carries the rest of its look (no side padding, a
   bottom hairline). This selector deliberately has NO space before its `{`
   (unlike every other rule in the file): a test finds "the" `.ask-chat`
   rule with the regex `\.ask-chat \{`, and a space here would make it match
   THIS rule instead of the one that actually carries the padding/border it
   is checking. Do not "fix" the spacing. */
.ask-panel-body > .ask-chat{ flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* Visual QA on the re-taken shots, 19 Sep: inside the panel the chat kept the
   inline block's bottom hairline (a stray line floating above the panel's
   edge) and its 360px thread cap (dead space under the box while a five-task
   answer scrolled). Here the thread takes whatever the panel has, and a panel
   with nothing asked yet is only as tall as what is in it. */
#ask-panel .ask-chat{ border-bottom: 0; padding-bottom: 0; margin-bottom: 0; }
#ask-panel .ask-thread { flex: 1 1 auto; min-height: 0; max-height: none; }
@media (min-width: 641px) {
  #ask-panel:not(:has(.aexch)) { height: auto; }
}

@media (max-width: 640px) {
  /* B-1: a bottom sheet, full width, 44px+ everywhere including the close
     control the phone gets its own size for.

     Checker catch (screenshot review, 19 Sep): the shell's own bottom tab
     bar (`.pnav`, `--pnav-h`, ~56px + the safe-area inset) is ALSO fixed to
     the viewport's bottom edge below 900px, on every account — client and
     staff alike. A bare `bottom: 16px` sat the fab 16-68px up from the
     viewport floor, which is INSIDE that bar's own 0-56px band: the fab
     covered part of "Settings" rather than floating above it. The fab
     clears the bar by the same 16px gap every other floating control on
     this page already uses (`--fab-clear` is the equivalent for the page's
     own scroll padding); the OPEN panel, a bottom sheet, is meant to cover
     the bar while it is up. */
  /* Check 2 (23 Sep): the fab only cleared the fixed bottom nav, not the
     CLIENT-only sticky "Get these fixed" bar (`.gtf-sticky`) that sits
     above it on the Roadmap — the fab's own 52-72px band overlapped the
     top of that bar, covering part of its button. `--gtf-h` (set by the
     script above `.donow-sticky`/`#tlist`'s own padding, ledger.css:1770)
     is already the bar's real rendered height, 0px when it is absent, so
     adding it here is the same clearance the task list's own bottom
     padding already gets — never a second, drifting number. */
  #ask-fab { right: 16px; bottom: calc(var(--pnav-h) + var(--gtf-h) + 16px); }
  /* Checker on Phase 13 (M-4): stacked ABOVE the staff chat button it overlapped
     it by 10px on a phone. Beside it, as on a desktop. */
  #ask-fab.ask-fab-staff { right: 72px; bottom: 96px; }
  #ask-panel {
    right: 0; left: 0; bottom: 0; width: auto; max-width: none;
    height: auto; max-height: 85vh; border-radius: 12px 12px 0 0;
  }
  #ask-fab.ask-fab-staff ~ #ask-panel { right: 0; bottom: 0; }
  #ask-panel-close { min-width: 44px; min-height: 44px; font-size: 20px; }
}

/* ---------------- reports tab ---------------- */
.ask { border: 1px solid var(--line); border-radius: 9px; padding: 16px 18px; background: var(--panel); }
.ask .label { margin-bottom: 9px; }
.ask-row { display: flex; gap: 10px; align-items: flex-start; }
.ask-row textarea {
  flex: 1; min-width: 0; resize: vertical; font: inherit; font-size: 14px;
  color: var(--hi); background: var(--bg); border: 1px solid var(--line);
  border-radius: 7px; padding: 11px 13px; line-height: 1.45;
}
.ask-row textarea::placeholder { color: var(--low); }
.ask-row .btn-primary { padding: 12px 20px; margin-top: 1px; }
.ask-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.ask-chips .chip {
  cursor: pointer; text-align: left; font-family: inherit; font-size: 12px;
  line-height: 1.35; padding: 6px 10px;
  /* A suggested question is a sentence, not a tag: it has to be allowed to
     wrap, or it pushes the page sideways on a phone. */
  white-space: normal; max-width: 100%;
}
.ask-chips .chip:hover { border-color: var(--mid); color: var(--hi); }

.working { border-color: var(--note); }
.working .label { color: var(--note); }

.rep-t { font-size: var(--fs-5); margin: 0 0 4px; letter-spacing: -0.01em; }
.rep-sub { font-family: var(--font-mono); font-size: 11.5px; color: var(--low); margin: 0 0 16px; }

.tiles { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 14px; }
.tile { flex: 1 1 170px; min-width: 0; border: 1px solid var(--line); border-radius: 8px; padding: 14px 16px; background: var(--panel); }
.tile .tl { font-family: var(--font-mono); font-size: 10px; color: var(--low); letter-spacing: .09em; text-transform: uppercase; }
.tile .tv { font-size: 27px; font-weight: 600; margin-top: 7px; line-height: 1.1; }
.tile .td { font-family: var(--font-mono); font-size: 12.5px; margin-top: 5px; color: var(--mid); }
.tile .td.good { color: var(--good); } .tile .td.crit { color: var(--crit); }
.tile .tp { font-family: var(--font-mono); font-size: 10.5px; color: var(--low); margin-top: 3px; }

.readout { margin-top: 14px; border-left: 2px solid var(--note); }
.readout p { margin: 0; font-size: 14.5px; line-height: 1.65; color: var(--hi); }

.chart-key { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 10px; font-family: var(--font-mono); font-size: 11px; color: var(--mid); }
.chart-key span { display: inline-flex; align-items: center; gap: 6px; }
.chart-key i { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }
.chart-key .k-note { color: var(--low); margin-left: auto; }

.wrow { display: flex; gap: 10px; padding: 7px 0; border-top: 1px solid var(--soft); font-size: 12.5px; color: var(--mid); flex-wrap: wrap; }
.wrow:first-of-type { border-top: 0; }

.qrow { display: flex; gap: 12px; padding: 10px 16px; border-top: 1px solid var(--soft); color: var(--mid); flex-wrap: wrap; }
.qrow:hover { background: var(--panel2); }
.qrow .t { font-family: var(--font-mono); font-size: 11px; color: var(--low); width: 138px; flex-shrink: 0; }
.qrow .q { flex: 1; min-width: 200px; font-size: 12.5px; }
.qrow .s { font-family: var(--font-mono); font-size: 10.5px; }

@media (max-width: 620px) {
  .ask-row { flex-direction: column; }
  .ask-row .btn-primary { width: 100%; }
  .tile { flex: 1 1 100%; }
  .chart-key .k-note { margin-left: 0; width: 100%; }
}

/* ---------------------------------------------------------------------------
   Client self-serve: price chips, the platform banner, blocked-tab states and
   the approval queue. Added 13 Aug 2026.
   --------------------------------------------------------------------------- */

/* A price on an issue row. Greyed, not hidden, on a site we cannot change:
   removing it would hide the value along with the button. */
.pchip { font-family: var(--font-mono); font-size: 11px; color: var(--hi);
  border: 1px solid var(--line); border-radius: 3px; padding: 1px 7px; white-space: nowrap; }
.pchip.off { color: var(--low); border-style: dashed; }

/* The platform recommendation. Sits above the tabs, dismissible for the session. */
.pbanner { border: 1px solid var(--note); border-radius: 10px; padding: 16px 18px;
  margin: 0 0 16px; background: var(--panel); }
/* Phase 5 (Hers): a confirmation is calm — a tinted ground and a green
   hairline, never a shouting border */
.pbanner.good { border-color: var(--line); background: var(--good-bg); }
.pbanner-t { font-family: var(--font); font-size: var(--fs-4);
  margin-bottom: 6px; }
.pbanner-b { font-size: 14px; line-height: 1.6; color: var(--mid); margin: 0 0 12px;
  max-width: 720px; }
.pbanner-a { display: flex; gap: 10px; flex-wrap: wrap; }

/* A tab that cannot answer its question yet. Never an empty chart, never a zero. */
.waitcard { border: 1px solid var(--soft); border-radius: 10px; padding: 34px;
  background: var(--panel); text-align: center; }
.waitcard h2 { font-size: 22px; margin: 0 0 10px; }
.waitcard p { font-size: 15px; line-height: 1.62; color: var(--mid); margin: 0 auto 16px;
  max-width: 520px; }

/* The approval queue. */
.apr { border-top: 1px solid var(--soft); padding: 14px 0; }
.apr:first-of-type { border-top: none; }
.apr-1 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 14px; }
.apr-a { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.apr-n { background: var(--bg); color: var(--hi); border: 1px solid var(--line);
  border-radius: 6px; padding: 6px 10px; font: inherit; font-size: 12.5px; min-width: 200px; }

/* The pairing code the client reads off the screen and types into the plugin. */
.paircode { font-family: var(--font-mono); font-size: 30px; letter-spacing: .18em;
  color: var(--hi); background: var(--bg); border: 1px dashed var(--note);
  border-radius: 8px; padding: 12px 18px; display: inline-block; margin: 8px 0; }
.steps { margin: 10px 0 6px; padding-left: 20px; }
.steps li { font-size: 13.5px; line-height: 1.7; color: var(--mid); }
.btn-sm { font-size: var(--fs-1); padding: 6px 11px; }

/* The issue title in the detail panel is a long, unbroken line on a phone: a
   product name plus a search volume, with no space the browser will break at.
   It was clipping mid-word against the panel edge. */
.idetail-h h3 { overflow-wrap: normal; word-break: normal; line-height: 1.35; }

/* The client-facing panels are the last thing on their page, so the floating
   buttons sit on top of their final line exactly the way they did on the issue
   panel. Same clearance, same reason. */
.connrow:last-of-type, .waitcard { scroll-margin-bottom: var(--fab-clear); }
@media (max-width: 760px) {
  /* The connections list and the wait card are the bottom of their page on a
     phone. Without this the last card's text runs under the floating buttons. */
  .tbody > .panel:last-of-type { margin-bottom: var(--fab-clear); }
  .waitcard { margin-bottom: var(--fab-clear); }
  .pbanner-a { row-gap: 8px; }
}

/* A client sees neither floating button, so their pages do not need to reserve
   the space for them. Reclaiming it removes a screenful of dead white at the
   bottom of every board. */
body.no-fab { --fab-clear: 24px; }
/* ==================================================== reports: a real chat
   Feedback 2026-08-12, planmanagers reports tab: "the chat box needs to operate
   like a normal chat (like it happens on slack) ... The result can be populated
   on the right side so you need to divide the screen into half. Look at Letido,
   do it like that."

   Half and half on a desktop, each side scrolling on its own so the composer
   stays under his thumb while a long answer scrolls beside it. One column on a
   phone, chat first. */
.csplit {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 18px;
  align-items: stretch;
  /* the tab's usual 152px foot exists to clear the floating feedback button; a
     split view with its own scrollers does not need it and looks broken with it */
  padding-bottom: 22px;
}
.cpane, .rpane { height: calc(100vh - 252px); min-height: 460px; }

.cpane {
  display: flex; flex-direction: column; min-width: 0; overflow: hidden;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel);
}
.cstream {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 18px;
  display: flex; flex-direction: column;
}
/* A short conversation sits at the BOTTOM, against the composer, the way every
   chat does — rather than at the top with a hole under it. Done with auto margin
   on the first child instead of justify-content: flex-end, which in a scroller
   makes the overflowing top unreachable. */
.cstream > :first-child { margin-top: auto; }
/* Layout polish, 2026-09-08: that bottom-anchoring is exactly what left a
   ~360px blank panel ABOVE "Ask about …" on a genuinely fresh reports tab —
   the pane still stretched to the grid's tall fixed height with nothing in
   it to anchor. `cpane-empty` (set in the template only when there is no
   chat at all) drops the fixed height and the anchoring, so the pane fits
   its own short content instead; `align-self: start` keeps CSS grid's own
   row-stretch from pulling it back up to match `.rpane` beside it. */
.cpane.cpane-empty { height: auto; min-height: 0; align-self: start; }
.cpane.cpane-empty .cstream { flex: 0 0 auto; overflow-y: visible; }
.cpane.cpane-empty .cstream > :first-child { margin-top: 0; }
/* The floating feedback and chat buttons sit over this pane's bottom-right
   corner, so its content needs room to scroll clear of them. The tab's usual
   152px foot does that job on a single column; here the pane is its own
   scroller, so the clearance has to live on the pane. */
.rpane { min-width: 0; overflow-y: auto; padding: 0 14px 96px 0; } /* right gutter matches the left pane's inset (checker 2026-09-04: the rail sat flush on the viewport edge at 1440) */
/* the two side-by-side panels at the foot of a report have room for one column
   in half a screen, not two */
.rpane .panel-row .panel { flex: 1 1 100%; }

/* ---- messages ---- */
.msg { margin-bottom: 15px; }
.msg:last-child { margin-bottom: 2px; }
.mhead { display: flex; align-items: baseline; gap: 8px; margin-bottom: 5px; }
.mhead .who, .mhead .when {
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .04em; color: var(--low);
}
.mhead .who { text-transform: uppercase; }
.mhead .who.a { color: var(--note); }
.bub {
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 13px;
  font-size: 13.5px; line-height: 1.62; color: var(--mid);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.msg.you .bub { background: var(--panel2); color: var(--hi); }
.bub.warn { border-color: var(--warn); }
.bub.run { border-color: var(--note); display: flex; gap: 10px; align-items: flex-start; }

/* Three dots, so "working on it" is visibly alive. A static sentence for fifty
   seconds is what he read as a hang. */
.dots { display: inline-flex; gap: 3px; flex-shrink: 0; padding-top: 8px; }
.dots i {
  width: 4px; height: 4px; border-radius: 50%; background: var(--note);
  animation: cblink 1.4s infinite both;
}
.dots i:nth-child(2) { animation-delay: .2s; }
.dots i:nth-child(3) { animation-delay: .4s; }
@keyframes cblink { 0%, 80%, 100% { opacity: .25 } 40% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) { .dots i { animation: none; opacity: .75 } }

/* What an answer was measured from, so two answers with different numbers for
   what looks like the same metric are readable as different questions. */
.bmeta {
  margin-top: 5px; font-family: var(--font-mono); font-size: 10px;
  color: var(--low); line-height: 1.5;
}

.mopen {
  display: inline-block; margin-top: 7px; text-decoration: none;
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .03em; text-transform: uppercase; color: var(--note);
  border-bottom: 1px solid transparent;
}
.mopen:hover { border-bottom-color: var(--mid); }

/* Which answer the figures currently belong to. It lives in the message header,
   beside "ledger", because under the bubble it read as a heading over a block of
   figures that is not there. */
.shown-tag {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .05em;
  text-transform: uppercase; color: var(--note);
  border: 1px solid var(--note); border-radius: 3px; padding: 0 5px; opacity: .75;
}

/* ---- an empty conversation ---- */
.cempty { padding: 6px 2px; }
.ce-t { font-size: 17px; color: var(--hi); margin-bottom: 8px; letter-spacing: -0.01em; }
.ce-s { font-size: 12.5px; line-height: 1.7; color: var(--mid); margin: 0 0 9px; }

/* ---- the composer ---- */
.composer {
  flex: 0 0 auto; border-top: 1px solid var(--line);
  padding: 12px 14px 13px; background: var(--panel);
}
.composer .ask-chips { margin: 0 0 10px; }
.cform { display: flex; gap: 10px; align-items: flex-end; }
.cform textarea {
  flex: 1; min-width: 0; resize: none;
  border: 1px solid var(--line); border-radius: 8px; background: var(--bg);
  color: var(--hi); font-family: inherit; font-size: 13.5px; line-height: 1.5;
  padding: 11px 12px;
}
.cform textarea:focus { outline: none; border-color: var(--note); }
.cform textarea::placeholder { color: var(--low); }
.cform textarea:disabled { opacity: .55; cursor: not-allowed; }
.cform .btn-primary { flex-shrink: 0; padding: 11px 18px; }
.cnote {
  display: flex; flex-wrap: wrap; gap: 4px 14px; justify-content: space-between;
  margin: 9px 0 0; line-height: 1.5;
  font-family: var(--font-mono); font-size: 10.5px; color: var(--low);
}
.cfresh { display: inline-flex; gap: 7px; align-items: baseline; }
.cfresh form { display: inline; }
.lnk {
  background: none; border: 0; padding: 0; cursor: pointer; color: var(--note);
  font-family: inherit; font-size: inherit; text-decoration: underline;
}

/* ---- nothing selected on the right ---- */
.rempty { border: 1px dashed var(--line); border-radius: 9px; padding: 22px; }
.rempty .note-t { margin: 0; }

/* ---- the issues tab task list (B2, 2026-09-03) ----
   The left pane is a scannable priced backlog by default, not a chat window:
   natural height, page scroll, Ask-the-board folded into a strip above it. */
.ipane {
  height: auto; min-height: 0; overflow: visible; display: block; padding: 14px;
}
/* P3 (redesign brief): Tasks kept ONE ask-the-board chip and its inline,
   collapsible answer — the composer/presets/history box this replaced
   (`.composer-strip`/`.cstream-mini`, both retired) moved wholesale to
   Results › Ask. Same bordered-strip language, a fraction of the markup. */
.ask-inline {
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel2);
  padding: 0; margin-bottom: 14px; overflow: hidden;
}
.ask-inline summary {
  cursor: pointer; list-style: none; padding: 11px 14px; font-size: 13px;
  font-weight: 600; color: var(--hi); display: flex; align-items: center; gap: 8px;
}
.ask-inline summary::-webkit-details-marker { display: none; }
.ask-inline summary::before {
  content: "▾"; color: var(--low); font-size: 10px;
}
.ask-inline-b { padding: 0 14px 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.ask-inline .lnk { margin: 0 14px 12px; }
.tlist-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  margin: 8px 2px 8px;
}
.tlist {
  border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: var(--panel);
}
.tlist .irow:last-child { border-bottom: 0; }
.tlist-more {
  display: block; text-align: center; padding: 12px; font-size: 12.5px;
}
/* Demoted footer line for another full audit (B2) — a footnote, not a band. */
.audit-mini {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  flex-wrap: wrap; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line);
}
.audit-mini .note-t { margin: 0; }
@media (max-width: 900px) {
  /* Layout polish, 2026-09-08: room to clear the mobile sticky purchase bar
     (`.donow-sticky`, see the --donow-sticky-clear comment above) when a
     task is open right above this footer line. */
  .audit-mini { padding-bottom: var(--donow-sticky-clear); }
}

/* S-6 (PHASE10_BRIEF.md H-05, 17 Sep, Ben UAT): the LIST itself never
   reserved room for the sticky chrome sitting over it on a phone, so
   scrolling to the last task could leave its bottom edge under the bar
   instead of above it. `--donow-sticky-clear` (76px, above) is sized for the
   STAFF-only `.donow-sticky` bar; a client's sticky bar is `.gtf-sticky`
   (project.html), which on a phone becomes a FIXED bottom bar over the
   `--pnav-h` bottom nav.
   F-S2 (fix round, 17 Sep): a live measurement (`_measure_tlist_clear.py`
   against the restarted app) found the first attempt — a flat
   `calc(var(--pnav-h) + 96px)`, copied off `.pmain`'s own clearance, tuned
   for a 44px tap target — 4px short of the REAL stack height on this board
   (156px measured: pnav-h plus the bar's actual rendered height, which
   varies with its content). `--gtf-h` is a token for that bar's height
   PLUS its own 16px gutter (folded in by project.html's script, so this
   formula never adds a gutter for a bar that turns out not to exist — see
   B-9 below); the fallback here is generous enough to cover the gap on its
   own before any JS runs, and project.html's own small script (next to
   `.gtf-sticky`'s styles) refines it to the bar's live
   `getBoundingClientRect().height` on load and resize.
   B-9 (Ben fold-in, 17 Sep): `.gtf-sticky` is CLIENT ONLY (staff use
   `.donow-sticky` instead, and only while a task is open) — a staff phone
   view was reserving ~173px for a bar that was never there. The same
   script sets `--gtf-h: 0px` when `.gtf-sticky` is absent from the page,
   so this formula reduces to `--pnav-h` alone for staff. */
:root { --gtf-h: 117px; }
@media (max-width: 640px) {
  #tlist { padding-bottom: calc(var(--pnav-h) + var(--gtf-h)); }
  .irow-line {
    scroll-margin-bottom: calc(var(--pnav-h) + var(--gtf-h));
    /* No sticky top bar exists over this list on a phone today (the
       roadmap header scrolls away with the page) — a small gutter only,
       so a row opened by a deep link never lands flush against the very
       top edge of the viewport. */
    scroll-margin-top: 12px;
  }
}

/* One column on a phone, chat first, then the figures under it.

   The panes stop being fixed-height, but the transcript keeps a capped inner
   scroll so the composer stays on screen instead of sitting below a long
   conversation — the standard mobile-chat shape. Capped at 60vh rather than
   filling the screen, so the page scroll is still obviously available and he is
   never stuck inside the inner one; both directions are asserted in the QA. */
@media (max-width: 1000px) {
  .csplit { grid-template-columns: 1fr; gap: 14px; padding-bottom: var(--fab-clear); }
  .cpane, .rpane { height: auto; min-height: 0; }
  .rpane { padding-bottom: 0; }
  .cstream { max-height: 60vh; }
  .cnote { justify-content: flex-start; }
  /* The rpane's own scroller (overflow-y:auto, set for the fixed-height
     desktop split) would otherwise pin the sticky purchase bar (B5) to the
     bottom of ITS box instead of the viewport once the pane grows past
     screen height on a phone. Auto-height means it is not its own scroll
     container any more, so this turns that off to match. */
  .rpane { overflow-y: visible; }
}

/* Clearance for the two floating buttons on every STAFF page, not just the
   issue panel. The portfolio, the clients list and the approvals queue are all
   .wrap pages, and their last card was running under the buttons: the QA pass
   read the Park Road card's "clicks not measurable" line as truncated, because
   on a phone it was. A client sees neither button, so body.no-fab collapses
   this back to normal padding for them. */
.wrap { padding-bottom: var(--fab-clear); }
body.no-fab .wrap { padding-bottom: 24px; }

/* The issue detail panel's own content column sat under the chat button on a
   wide screen too: the panel is fixed-width on the right, which is exactly
   where the buttons are. */
/* Fix round (checker M4): retired, `.app-scroll` carries the clearance.
   (was: .idetail-b { padding-bottom: calc(fab-clear + 12px) } at >= 761px) */

/* The two floating buttons are fixed to the bottom-right of the VIEWPORT, so
   they pass over whatever happens to be scrolled under them. Page-bottom
   padding does not help: mid-scroll they were sitting on the end of a card's
   own text ("clicks not measurabl…", "crawled 2026-08-1…") and over the health
   figure on the card below. On a narrow screen the content now stops short of
   that column, so the buttons pass over card whitespace instead of words.
   A client sees neither button, so no-fab gives them the full width back. */
@media (max-width: 620px) {
  .pcard, .connrow, .apr { padding-right: 58px; }
  body.no-fab .pcard, body.no-fab .connrow, body.no-fab .apr { padding-right: 16px; }
}

/* ---- plans (Settings › billing, /pricing) ---- */
.plan-toggle { margin: 8px 0 12px; }
.plan-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; }
.plan-card { border: 1px solid var(--line); border-radius: 10px; padding: 20px; background: var(--panel2); }
.plan-card-current { border-color: var(--note); }
.plan-name { font-family: var(--font); font-size: var(--fs-5); font-weight: 700; color: var(--hi); }
.plan-price { font-family: var(--font-mono); font-size: var(--fs-6); color: var(--hi); margin: 8px 0 12px; }
.plan-price .plan-per, .plan-price .plan-save { font-size: var(--fs-2); color: var(--mid); font-family: var(--font); }
.plan-price .plan-save { color: var(--good); }
/* Phase 4: while the prices are unconfirmed the price slot holds a
   sentence, not a figure — set in the body face at note size so it reads
   as a note, not a 22px price. */
.plan-price.plan-pending { font-family: var(--font); font-size: var(--fs-2); color: var(--mid); line-height: 1.5; }
.plan-includes { margin: 0 0 16px; padding-left: 18px; color: var(--mid); font-size: var(--fs-3); line-height: 1.6; }
.plan-gate { border: 1px solid var(--note); border-radius: 10px; padding: 20px; }
/* the gate's one action and its note — was two inline styles on three copies */
.plan-gate .btn-primary { display: inline-block; margin-top: 8px; }
.plan-gate .note-t { margin: 8px 0 0; }

/* ---- credits (retired 2026-09-12; rules kept only for the owner's history table) ---- */
.creditbar { display: flex; gap: 40px; flex-wrap: wrap; border: 1px solid var(--line);
  border-radius: 10px; padding: 18px 22px; background: var(--panel); margin-bottom: 14px; }
.creditnum { font-family: var(--font-mono); font-size: 34px; line-height: 1.1;
  color: var(--hi); margin-top: 4px; }
.creditplan { font-size: 14px; color: var(--mid); margin-top: 10px; }
.topups { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.topup { display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: var(--bg); border: 1px solid var(--line); border-radius: 9px;
  padding: 14px 20px; color: var(--hi); min-width: 96px; }
.topup:hover { border-color: var(--mid); }
.topup .tc { font-family: var(--font-mono); font-size: 22px; }
.topup .tl { font-size: 10.5px; color: var(--low); text-transform: uppercase; letter-spacing: .08em; }
.topup .tp { font-size: 13px; color: var(--mid); margin-top: 4px; }
.topup .tb { margin-top: 8px; font-family: var(--font-mono); font-size: 10.5px;
  color: var(--accent-ink); background: var(--accent); border-radius: 4px; padding: 3px 12px; }

/* The detail panel's action bar is sticky at the bottom of the panel, so the
   scrolling content underneath must reserve at least its height or the bar
   permanently covers the last thing on the card. It did: a client's board sets
   --fab-clear to 24px (they have no floating buttons), which is less than the
   bar is tall, and the third option of a decision card was underneath it. The
   floor is the bar, not the buttons. */
/* Fix round (checker M4): the bar is no longer sticky (see `.idetail-f`),
   so the floor is the content's own padding, not a 152px band. */
.idetail-b { padding-bottom: 16px; }
@media (max-width: 620px) {
  /* Four top-up cards wrap 3 + 1 and orphan the last one. Two by two is even. */
  .topups { display: grid; grid-template-columns: 1fr 1fr; }
  .topups form, .topup { width: 100%; }
}

/* The two floating buttons were stacked close enough to touch on a phone, and
   the lower one is dark on a dark page, so it read as one smudged control. */
@media (max-width: 620px) {
  #chat-fab { bottom: 96px; }
}

/* The pairing watcher: the page waits for the site to connect rather than
   asking the client to tell us they did it. */
.pairwatch { display: flex; align-items: center; gap: 10px; margin-top: 14px;
  border: 1px solid var(--line); border-radius: 8px; padding: 12px 14px;
  background: var(--bg); font-size: 13.5px; color: var(--mid); max-width: 640px; }
.pairwatch .dot { width: 8px; height: 8px; border-radius: 999px;
  background: var(--note); flex-shrink: 0; }
.pairwatch.bad { border-color: var(--crit); color: var(--crit); }
.pairwatch.bad .dot { background: var(--crit); }

/* Issue filters. Same shape as the rankings filters so the board has one idiom. */
.ifilter { display: flex; gap: 4px; padding: 0 22px 10px; flex-wrap: wrap; }
.ifilter a { font-family: var(--font-mono); font-size: 11px; color: var(--low);
  padding: 5px 10px; border-radius: 5px; border: 1px solid transparent; }
.ifilter a.on { color: var(--hi); border-color: var(--line); background: var(--raise); }
.ifilter a:hover { color: var(--hi); }
.ifilter .n { color: var(--note); }
/* Two rows of pills asking two different questions — what kind of thing it is,
   then what state it is in. The hairline is doing real work: without it the
   rows wrap into each other on a narrow screen and read as one long filter. */
.kfilter { padding-bottom: 9px; margin-bottom: 9px; border-bottom: 1px solid var(--line); }

/* A change the client asked for, marked as theirs and linked back to the task.
   Everything else in this tab is something we noticed; this is something they
   chose, which is a different relationship to the number underneath it. */
.cz-req { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 6px 0 2px; font-size: 12.5px; }
.cz-reqtag { font-family: var(--font); font-size: 10px; color: var(--good);
  background: var(--good-bg); border-radius: 3px; padding: 2px 7px; white-space: nowrap; }
.cz-req a { color: var(--hi); text-decoration: underline; text-underline-offset: 2px; }

/* Connected, with the way back out sitting next to it. */
.connlive { display: inline-flex; align-items: center; gap: 10px; }

/* The move-platform recommendation. Argued from the client's own market, so it
   is given the weight of a finding rather than the styling of an advert. */
.advice { border: 1px solid var(--note); border-radius: 10px; padding: 18px 20px;
  background: var(--panel); margin-bottom: 14px; }
.advice-h { font-family: var(--font); font-size: 16px;
  margin-bottom: 8px; }
.advice-p { font-size: 14px; line-height: 1.6; color: var(--mid); margin: 0 0 10px;
  max-width: 720px; }
.advice-ev { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }
.advice-chip { font-family: var(--font-mono); font-size: 11px; color: var(--hi);
  border: 1px solid var(--line); border-radius: 4px; padding: 3px 9px; }
.advice-a { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* Type-to-narrow, on every long list (Harsha 2026-08-14: "there should be a
   search bar which when users start typing narrows down the issue theyre
   searching for. Apply to all projects"). One idiom for the issues board, the
   tracked keywords and the change ledger, so the box looks the same wherever
   a list is long enough to need it. */
.lsearch { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.lsearch-i { font-family: var(--font-mono); font-size: 11.5px;
  background: var(--bg); border: 1px solid var(--line); border-radius: 5px;
  padding: 5px 9px; width: 210px; max-width: 100%; }
.lsearch-i:focus { outline: none; border-color: var(--note); }
.lsearch-i::placeholder { color: var(--low); }
.lsearch-n { font-family: var(--font-mono); font-size: 10.5px; color: var(--mid); }
.lsearch-n[hidden], .lsearch-x[hidden] { display: none; }
.lsearch-x { font-family: var(--font-mono); font-size: 10.5px; color: var(--low);
  background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline;
  text-underline-offset: 2px; }
.lsearch-x:hover { color: var(--hi); }
/* On the change ledger the box sits on its own line above the list: that tab
   has no filter row to share. */
.lsearch-bar { display: flex; justify-content: flex-end; margin: 0 0 8px; }

/* A row the search has ruled out. Hidden, never removed: deleting a letter has
   to widen the list again without a trip to the server. */
.lshide { display: none !important; }
.lsearch-none { padding: 18px 22px; }
.lsearch-none[hidden] { display: none; }
/* Paginated past the first page (rankings, B3 2026-09-03) — same "hide, never
   remove" rule as a search miss, so "show all" doesn't need a round trip. */
.pghide { display: none !important; }
/* Sticky header on a long table (B3, 2026-09-03) — client and staff alike. */
.stick-table thead th {
  position: sticky; top: 0; z-index: 2; background: var(--panel);
  box-shadow: 0 1px 0 var(--line);
}

/* On a phone the box takes the width rather than squeezing in beside the
   filters — a 210px input next to four pills is neither. */
@media (max-width: 640px) {
  .lsearch { margin-left: 0; width: 100%; }
  .lsearch-i { flex: 1; width: auto; }
}

/* The plain-English lead on a live finding: what it means for the business
   before any evidence. Shares its words with the owner preview app. */
.ev-plain {
  border: 1px solid var(--line); border-left: 3px solid var(--note);
  background: var(--soft); border-radius: 5px;
  padding: 11px 13px; margin: 0 0 16px;
}
.ev-plain-h { font-size: 13.5px; font-weight: 600; color: var(--hi); margin: 0; }
.ev-plain .ev-prose { color: var(--mid); }

/* ---- owner-first issues board (2026-08-19): one column, theme cards, the
   detail full-width under its row instead of a side panel ---- */
.ilist.standalone { border: 0; min-height: 0; }
.gsec {
  font-size: 15px; font-weight: 650; color: var(--hi);
  margin: 20px 2px 10px; letter-spacing: .01em;
}
.gsec:first-child { margin-top: 4px; }
.gsec-s { font-size: 12px; font-weight: 400; color: var(--mid); margin-left: 8px; }
/* The site-changes section behind a toggle for a client (B3, 2026-09-03) — a
   <summary> wearing .gsec keeps the same heading look, plus a cursor and a
   chevron so the disclosure reads as one. */
summary.gsec { cursor: pointer; list-style: none; }
summary.gsec::-webkit-details-marker { display: none; }
summary.gsec::before { content: '▸ '; color: var(--low); font-size: 12px; }
.gsec-toggle[open] > summary.gsec::before { content: '▾ '; }

/* Outcome distribution strip — one thin stacked bar, status colours only,
   each segment with a text label alongside it (B3, 2026-09-03). */
.outcome-strip {
  display: flex; gap: 2px; height: 10px; border-radius: 3px; overflow: hidden;
  margin-top: 10px; background: var(--line);
}
.outcome-strip span { display: block; min-width: 3px; }

/* From → To chips for a text change (redesign brief P5b, Toggl Track). */
.fromto { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.fromto-c { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fromto-arrow { color: var(--low); font-size: 13px; }

/* Per-change before→after bar pair, 14d clicks (B3, 2026-09-03). */
.pp-bars { display: flex; flex-direction: column; gap: 4px; margin: 6px 0 2px; max-width: 360px; }
.pp-row { display: flex; align-items: center; gap: 8px; }
.pp-l {
  width: 40px; flex: 0 0 auto; font-family: var(--font-mono); font-size: 10px;
  color: var(--low); text-transform: uppercase; letter-spacing: .04em;
}
.pp-track { flex: 1 1 auto; height: 8px; background: var(--soft); border-radius: 3px; overflow: hidden; }
.pp-bar { display: block; height: 100%; border-radius: 3px; min-width: 3px; }
.pp-bar.pp-pending {
  width: 55%; background: repeating-linear-gradient(45deg, var(--ch-warn), var(--ch-warn) 3px, transparent 3px, transparent 7px);
  opacity: .6;
}
.pp-v { flex: 0 0 auto; font-family: var(--font-mono); font-size: 11px; color: var(--mid); min-width: 28px; text-align: right; }

/* Position-bucket bar, rankings tab (B3, 2026-09-03) — ordinal, one hue in
   monotone opacity steps (1–3 most prominent, 21+ least), never four
   different series colours for a set where the ORDER carries the meaning. */
.bucket-bar { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.bucket-row { display: flex; align-items: center; gap: 8px; }
.bucket-l {
  width: 42px; flex: 0 0 auto; font-family: var(--font-mono); font-size: 11px;
  color: var(--mid);
}
.bucket-track { flex: 1 1 auto; height: 12px; background: var(--soft); border-radius: 3px; overflow: hidden; }
.bucket-fill { display: block; height: 100%; border-radius: 3px; min-width: 3px; }
.bucket-v { flex: 0 0 auto; font-family: var(--font-mono); font-size: 11px; color: var(--hi); min-width: 24px; text-align: right; }
.gcard {
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel);
  margin: 0 0 12px; overflow: hidden;
}
.gcard-h {
  list-style: none; cursor: pointer; padding: 14px 16px;
  display: flex; flex-direction: column; gap: 4px;
}
.gcard-h::-webkit-details-marker { display: none; }
.gcard-h::after {
  content: '▾'; position: absolute; right: 18px; margin-top: 2px;
  color: var(--mid); font-size: 12px; transition: transform .15s;
}
.gcard { position: relative; }
.gcard[open] > .gcard-h::after { transform: rotate(180deg); }
.gcard-t { font-size: var(--fs-3); font-weight: 600; color: var(--hi); padding-right: 30px; }
.gcard-chips { font-size: var(--fs-2); color: var(--note); }
.gcard-pts { color: var(--good); font-weight: 600; }
.gcard-m { font-size: var(--fs-2); color: var(--mid); line-height: 1.55; max-width: 860px; }
.gcard-b { border-top: 1px solid var(--line); }
.gcard-b .irow { border-left: 0; border-right: 0; }
.gcard-b .irow:last-child { border-bottom: 0; }
/* A ghost sample row on a blocked Changes tab (B3, 2026-09-03) — shaped like
   the real card, dashed and dimmed so it never reads as an actual change,
   and not collapsible (no chevron) since there is nothing under it to open. */
.gcard.ghost { border-style: dashed; opacity: .55; margin-top: 14px; }
.gcard.ghost .gcard-h::after { content: none; }

/* the detail, inline and full width — same bones as the old side panel */
.idetail.inline {
  width: auto; border-left: 0; display: block;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  background: var(--soft);
}
.idetail.inline .idetail-b { overflow: visible; padding-bottom: 18px; }
.idetail.inline .ev-pages, .idetail.inline .ev-prose { max-width: 980px; }

/* pages as a table: one page per row, the measurement beside it */
/* F5 (fix round 1): a client's affected-URL list is never cut at 8 — it
   scrolls inside a bounded box instead, so a 30-page task stays one panel. */
.ev-table-scroll { max-height: 360px; overflow-y: auto; max-width: 980px; }
.ev-table { width: 100%; max-width: 980px; border-collapse: collapse; font-size: 12.5px; }
.ev-table td { padding: 6px 10px 6px 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.ev-table td a { overflow-wrap: anywhere; }
.ev-table-n { color: var(--mid); white-space: normal; width: 45%; }
/* Stacked cards on mobile (B5, 2026-09-03) — a URL table that would
   otherwise need a sideways swipe becomes one card per row instead. */
@media (max-width: 640px) {
  .ev-table, .ev-table tbody, .ev-table tr, .ev-table td { display: block; width: auto; }
  .ev-table tr:first-child { display: none; }             /* the header row */
  .ev-table tr {
    border: 1px solid var(--line); border-radius: 7px; padding: 8px 10px;
    margin-bottom: 8px; background: var(--panel);
  }
  .ev-table td { border-bottom: 0; padding: 3px 0; }
  .ev-table td[data-th]::before {
    content: attr(data-th); display: block; font-family: var(--font-mono);
    font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--low);
    margin-bottom: 2px;
  }
  .ev-table-n { width: auto; }
}
.ev-more > summary { list-style: none; }
.ev-more > summary::-webkit-details-marker { display: none; }
.ev-more > summary::after { content: ' ▾'; color: var(--mid); }
.ev-more[open] > summary::after { content: ' ▴'; }
/* Connections tab body copy, tucked behind a click (B3, 2026-09-03). */
.setup-more { margin-top: 6px; }
.setup-more > summary { list-style: none; cursor: pointer; font-size: 12px; }
.setup-more > summary::-webkit-details-marker { display: none; }
/* A2 (Phase 14, 23 Sep): the "I don't have this, set it up for me" link
   opens this INLINE, in place — no navigation, nothing shown until it is
   open. */
.setup-confirm { margin-top: 8px; max-width: 420px; }
.setup-confirm-a { display: flex; gap: 10px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
/* The outcome-based sub-label under "Google Search Console" / "Google
   Analytics" (A2, Phase 14) — the same words every other screen still uses. */
.conn-sublabel { margin-left: 6px; font-weight: 400; }

/* ---- priorities released in rounds (2026-08-25) ---- */
.rounds { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }
.round {
  font-family: var(--font-mono); font-size: 11px; color: var(--mid);
  border: 1px solid var(--line); border-radius: 999px; padding: 4px 10px; background: var(--panel);
}
.round b { font-weight: 600; }
.gcard.pri { border-color: color-mix(in srgb, var(--note) 45%, var(--line)); }
.gcard-out { font-size: 12.5px; color: var(--hi); line-height: 1.5; max-width: 860px; margin-top: 2px; }
.gcard-out b { font-weight: 600; color: var(--mid); }
.gcard-prog { display: flex; align-items: center; gap: 10px; margin-top: 4px; }
.gbar { flex: 0 0 160px; height: 6px; border-radius: 999px; background: var(--line); overflow: hidden; }
.gbar i { display: block; height: 100%; background: var(--good); border-radius: 999px; }
.gbar-t { font-size: 11.5px; color: var(--mid); }
.gcard-cta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 16px; border-bottom: 1px solid var(--line); background: var(--soft); }
.loadmore { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-top: 12px; }
.loadmore .lm-l { flex: 1; min-width: 240px; }
.loadmore .lm-ask { flex-basis: 100%; border-top: 1px solid var(--line); padding-top: 12px; }
.btn-primary.ghost { background: transparent; color: var(--note); border: 1px solid var(--note); }
.gheld .gcard { opacity: .85; }
@media (max-width: 640px) { .gbar { flex-basis: 110px; } }

/* ---- changes tab: ours vs other, Google factored in ---- */
.extnote {
  font-size: 12.5px; color: var(--mid); line-height: 1.55; border: 1px solid var(--warn);
  border-radius: 7px; padding: 10px 14px; margin: 0 0 12px; background: var(--panel);
}
.extnote b { color: var(--hi); }
.osec + .osec { border-top: 2px solid var(--line); }
.osec-h { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; padding: 14px 16px 2px; }
.osec-t { font-size: 15px; font-weight: 650; color: var(--hi); }
.osec-n { font-family: var(--font-mono); font-size: 11px; color: var(--mid); }
.osec-s { font-size: 12.5px; color: var(--mid); margin: 0; padding: 0 16px 8px; max-width: 820px; line-height: 1.5; }
.osec-sum { padding: 0 16px; margin-bottom: 8px; }
.osec-empty { padding: 6px 16px 14px; margin: 0; }
.vd-warn { color: var(--warn); }
/* the floating buttons sit bottom-right; the load-more button must not hide under them */
.loadmore { padding-right: 96px; }
@media (max-width: 640px) { .loadmore { padding-right: 16px; padding-bottom: 72px; } }

/* ---- the priorities view: cards only (second pass, 25 Aug) ---- */
.pback { display: inline-block; font-size: 12.5px; color: var(--mid); margin: 0 0 12px; }
.pback:hover { color: var(--hi); }
.pcard {
  border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
  padding: 18px 20px 16px; margin: 0 0 12px; max-width: 760px;
}
.pcard-top { display: flex; justify-content: space-between; gap: 10px; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase; color: var(--note); margin-bottom: 8px; }
.pcard-r { color: var(--low); text-transform: none; letter-spacing: 0; }
.pcard-h { font-size: 17px; font-weight: 650; color: var(--hi); line-height: 1.3; margin: 0 0 8px; }
.pcard-out { font-size: 13px; color: var(--mid); margin: 0 0 8px; line-height: 1.5; }
.pcard-out b { color: var(--mid); font-weight: 600; }
.pcard-m { font-size: 13px; color: var(--mid); line-height: 1.55; margin: 0 0 14px; }
.pcard-f { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-size: 12px; color: var(--mid); border-top: 1px solid var(--line); padding-top: 12px; margin-bottom: 12px; }
.pcard-n { color: var(--hi); }
.pcard-cta { display: block; width: 100%; text-align: center; box-sizing: border-box; }
.pcard.focus { max-width: none; margin-bottom: 0; }
.pfocus-list { padding: 0; overflow: hidden; }
.pfocus-list .irow { border-left: 0; border-right: 0; }
.btn-primary[disabled] { opacity: .45; cursor: not-allowed; }
.nums-cap { align-self: flex-end; }
.nums-cap .l { color: var(--low); font-style: italic; }

/* ---- Phase 2 (PIVOT_BRIEF.md §4): dual impact bars, roadmap pills, the
   section CTA bar, the confirm sheet, the receipt toast ---- */
/* Phase 5 (Perplexity Health): the two bars are thick and quiet — an 8px
   track, the two token fills, no ticks, no numbers on the row. */
.impbars { display: flex; gap: 16px; flex-wrap: wrap; margin: 8px 0 0 14px; }
.impbar { display: inline-flex; align-items: center; gap: 8px; min-width: 160px; flex: 1 1 160px; max-width: 280px; }
.impbar-l { font-family: var(--font-mono); font-size: var(--fs-1); color: var(--low); white-space: nowrap; width: 92px; flex-shrink: 0; }
.impbar-t { flex: 1; height: 8px; border-radius: 999px; background: var(--soft); overflow: hidden; }
.impbar-f { display: block; height: 100%; border-radius: 999px; }
.impbar-f.g { background: var(--bar-google); }
.impbar-f.a { background: var(--bar-ai); }
.impbars-legend { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 0 2px 12px; }
.impkey { width: 16px; height: 8px; border-radius: 999px; display: inline-block; margin-right: 6px; }
.impkey.g { background: var(--bar-google); } .impkey.a { background: var(--bar-ai); }
/* F9: a key and its word never part at a wrap; items wrap as whole units. */
.impkey-item { white-space: nowrap; display: inline-flex; align-items: center; }
/* Phase 5: ONE segmented control (Buffer's pricing toggle) for both the
   roadmap views and the Monthly | Annual switch — one track, the lit
   segment raised on --panel2, the rest borderless. The markup stays a row
   of `.chip`s (a test pins `chip rmap-pill on`), so this overrides the chip's
   own border and mono face; it scrolls sideways at 390 instead of wrapping
   into two rows, which is what a segmented control does. */
.rmap-filters, .plan-toggle {
  display: inline-flex; gap: 2px; padding: 3px; max-width: 100%; overflow-x: auto;
  border: 1px solid var(--line); border-radius: 999px; background: var(--panel);
  -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.rmap-filters::-webkit-scrollbar, .plan-toggle::-webkit-scrollbar { display: none; }
/* U3-27 (PHASE14_SOURCE.md): at 390px the filter strip's own scrollbar is
   hidden (line above) and "All", the last chip, sits past the edge with
   nothing saying there is more to scroll to — the same "wider than the
   screen with nothing saying so" problem `.scrollx` already solves for a
   table, on the exact same 760px breakpoint and the exact same fade. */
@media (max-width: 760px) {
  .rmap-filters { position: relative;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent); }
}
.rmap-filters .chip, .plan-toggle .chip {
  border: 0; background: transparent; border-radius: 999px; padding: 6px 12px; flex-shrink: 0;
  font-family: inherit; font-size: var(--fs-2); line-height: 1.3; color: var(--mid); white-space: nowrap;
  transition: background var(--motion), color var(--motion);
}
.rmap-filters .chip:hover, .plan-toggle .chip:hover:not(:disabled) { color: var(--hi); background: transparent; }
.rmap-filters .chip.on, .plan-toggle .chip.on { color: var(--hi); background: var(--panel2); box-shadow: inset 0 0 0 1px var(--line); }
.rmap-filters { margin: 0 0 12px; align-self: flex-start; }
.rmap-pill { text-decoration: none; }
/* Phase 5: the status chips are classes, one per state, so every row, the
   panel and the sheet paint a state the same way (they were five inline
   styles in the row macro). Same five tokens as before. */
.state-chip.st-open { color: var(--low); background: var(--raise); border-color: transparent; }
.state-chip.st-requested { color: var(--note); background: var(--note-bg); border-color: transparent; }
.state-chip.st-progress { color: var(--warn); background: var(--warn-bg); }
.state-chip.st-done { color: var(--good); background: var(--good-bg); }
.state-chip.st-verified { color: var(--good); background: var(--good-bg); border-color: transparent; }
.state-chip.st-declined { color: var(--mid); background: var(--raise); border-color: transparent; }
/* the priority word and dot on a row, by severity — the same three tokens
   the row macro used to write inline */
.sev-high, .sev-critical { color: var(--crit); }
.sev-medium { color: var(--warn); }
.sev-low { color: var(--note); }
.sevdot.sev-high, .sevdot.sev-critical { background: var(--crit); }
.sevdot.sev-medium { background: var(--warn); }
.sevdot.sev-low { background: var(--note); }
.sevdot.sev-none, .sevdot.sev- { background: var(--low); }
.sevdot.good-d { background: var(--good); } .sevdot.warn-d { background: var(--warn); } .sevdot.low-d { background: var(--low); }
.chip.filled.accent-c { background: var(--note-bg); color: var(--note); }
/* Phase 5: the sticky CTA bar floats over the list as it scrolls, so it is
   the one place on the roadmap that carries the shadow token; the primary
   button is the only filled thing in it. */
.gtf-bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  padding: 12px 16px; margin: 0 0 16px; background: var(--panel2); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: var(--shadow); }
.gtf-bar-l { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; min-width: 0; }
.gtf-lead { font-size: var(--fs-3); color: var(--mid); }
.gtf-sent { font-size: var(--fs-3); font-weight: 500; color: var(--hi); }
.gtf-cta { padding: 10px 18px; font-size: var(--fs-3); white-space: nowrap; }
.gtf-count { margin: 4px 0 12px; }
.gtf-timeline { list-style: none; display: flex; gap: 0; margin: 0; padding: 0; }
.gtf-timeline li { display: flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: var(--fs-1); color: var(--low); }
.gtf-timeline li + li::before { content: ""; width: 22px; height: 1px; background: var(--line); margin: 0 6px; }
.gtf-timeline li.on { color: var(--hi); }
.gtf-dot { width: 7px; height: 7px; border-radius: 999px; background: var(--line); display: inline-block; }
.gtf-timeline li.on .gtf-dot { background: var(--note); }
.gtf-when { color: var(--low); }
/* The sheet's form wraps .modal-b/.modal-f inside the flex card, so without
   this the body never gets the card's height limit and ten rows push the
   Send button below a phone's viewport (390, v15 screenshot). */
#m-gtf .modal-card > form { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.gtf-list { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.gtf-row { display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center; padding: 10px 12px; border-bottom: 1px solid var(--soft); cursor: pointer; }
.gtf-row:last-child { border-bottom: 0; }
.gtf-row-t { font-size: var(--fs-3); color: var(--hi); }
.gtf-row-m { font-family: var(--font-mono); font-size: var(--fs-1); color: var(--low); white-space: nowrap; }
@media (max-width: 900px) { .gtf-row { grid-template-columns: auto 1fr; } .gtf-row-m { grid-column: 2; white-space: normal; } }
.toast { position: fixed; left: 50%; top: 16px; transform: translateX(-50%); z-index: 70;
  display: flex; align-items: center; gap: 12px; max-width: calc(100% - 32px);
  padding: 12px 14px 12px 18px; border-radius: 10px; background: var(--panel2); border: 1px solid var(--line);
  box-shadow: var(--shadow); transition: opacity var(--motion), transform var(--motion); }
.toast.good { border-color: var(--good); }
.toast.gone { opacity: 0; transform: translate(-50%, -8px); }
.toast-t { font-size: var(--fs-3); color: var(--hi); }
.toast-s { font-family: var(--font-mono); font-size: var(--fs-1); color: var(--low); }
.toast-x { background: transparent; border: 0; color: var(--low); font-size: 18px; line-height: 1; cursor: pointer; }

/* ---- Phase 3 (PIVOT_BRIEF.md §5): the Live audit screen. Two facts, one
   trend strip, one list — the same panel/label/prose tokens as the rest of
   the board, nothing chart-like beyond a row of bars on the soft track. ---- */
.la-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 16px; }
.la-grid + .la-grid { margin-top: 16px; }
@media (max-width: 760px) { .la-grid { grid-template-columns: 1fr; } }
.la-card { border: 1px solid var(--line); border-radius: 8px; padding: 16px 18px; background: var(--panel2); min-width: 0; }
.la-card .ev-prose { margin: 4px 0 0; }
.la-sub { margin: 12px 0 4px; }
.la-big { font-family: var(--font); font-size: var(--fs-5); line-height: 1.25; color: var(--hi); margin: 2px 0 6px; overflow-wrap: anywhere; }
.la-score { color: var(--hi); font-weight: 600; }
/* Phase 5 (Sentry's always-on card): a live dot beside the headline says the
   audit is a running thing, not a report. The pulse honours reduced motion
   like every other one on the page. */
.la-live { display: inline-block; width: 8px; height: 8px; border-radius: 999px; background: var(--good);
  margin: 0 10px 2px 0; box-shadow: 0 0 0 3px var(--good-bg); }
.la-trend { display: flex; align-items: flex-end; gap: 4px; height: 88px; margin-top: 8px;
  padding: 0 0 1px; border-bottom: 1px solid var(--soft); }
.la-trend span { flex: 1 1 auto; min-width: 6px; max-width: 22px; background: var(--raise); border-radius: 3px 3px 0 0; }
/* Phase 5 (Tana): what changed is a timeline — a hairline down the left,
   a dot per item, accent for opened and --good for verified on site. */
.la-list { list-style: none; margin: 0; padding-left: 20px; font-size: var(--fs-3); line-height: 1.6; color: var(--mid); position: relative; }
.la-list::before { content: ''; position: absolute; left: 3px; top: 8px; bottom: 8px; width: 1px; background: var(--line); }
.la-list li { position: relative; padding: 2px 0; }
.la-list li::before { content: ''; position: absolute; left: -20px; top: 9px; width: 7px; height: 7px; border-radius: 999px; background: var(--note); }
.la-list.la-verified li::before { background: var(--good); }
.la-list li.note-t::before { background: var(--line); }
.la-list a { color: var(--hi); text-decoration: underline; text-decoration-color: var(--line); }
.la-list a:hover { text-decoration-color: var(--mid); }

/* ---- Phase 3: the Google recovery state (3.3) and the wait-state connect
   block (3.2). Same tokens as the connection rows; the steps are a plain
   numbered list because they are instructions, not a pitch. ---- */
.grec { max-width: 620px; }
.grec-lead { font-size: 14.5px; color: var(--hi); margin: 0 0 8px; line-height: 1.5; }
.grec-steps { margin: 0 0 12px; padding-left: 20px; font-size: 13px; line-height: 1.65; color: var(--mid); }
.grec-steps li + li { margin-top: 4px; }
.grec-steps b { color: var(--hi); font-weight: 600; }
.grec-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.grec-actions form { margin: 0; }
.wait-google { max-width: 560px; margin: 32px auto 0; padding: 24px 20px 20px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--panel); text-align: center; }
.wait-google-btns { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.wait-google-btns form { margin: 0; }
.wait-gnote { font-size: var(--fs-3); color: var(--warn); margin: 0 0 14px; line-height: 1.5; }
.wait-gnote.good { color: var(--good); }
.conn-prop { font-family: var(--font); font-size: var(--fs-2); color: var(--hi) !important; overflow-wrap: anywhere; }

/* ---- Phase 5 (PIVOT_BRIEF.md §10): the classes that replaced project.html's
   stray inline `style=` margins and paddings. Deliberately LAST in the file so
   a utility beats the atom it adjusts by source order, with no !important.
   Four margins from the 8-pt rhythm and the two or three named shapes the
   templates kept reaching for inline. ---- */
.panel.flush { padding: 0; }
.conn-note { margin: 6px 0 0; max-width: 520px; }
.conn-foot { max-width: 640px; margin: 16px 0 0; }
.plain-list { margin: 4px 0 8px; padding-left: 18px; }
.tlist-foot { margin-top: 12px; }
.nowrap { white-space: nowrap; }
.m0 { margin: 0; }
.mt4 { margin: 4px 0 0; }
.mt8 { margin-top: 8px; }
.mt16 { margin-top: 16px; }
.mt18 { margin-top: 18px; }
.mb8 { margin-bottom: 8px; }
.mb12 { margin-bottom: 12px; }
.mb16 { margin-bottom: 16px; }

/* =====================================================================
   Phase 6 (PHASE6_BRIEF.md, 2026-09-14): the LIGHT rebuild, matched to
   the ten Mobbin references in reviews/mobbin_refs/. Deliberately the LAST
   section of the file: a shared atom re-drawn here beats its earlier rule
   by source order alone, so the staff-only screens above keep their bones
   and inherit the tokens, while every client surface takes the reference
   shape. One section per surface, in the brief's order; each says which
   image it was taken from.
   ===================================================================== */

/* ---- 0. the page (Linear): a light grey ground, dark type, calm links.
   A link is the calm blue, underlined only in prose; the accent is never a
   link colour. The focus ring is the ink, not the accent. ---- */
body { background: var(--bg); color: var(--hi); font: 15px/1.5 var(--font); -webkit-font-smoothing: antialiased; }  /* 18 Oct 2026: 15px, the brand's body size (brand/BRAND.md §7); the earlier 14px here used to win by source order */
a { color: var(--note); }
.ev-prose a, .note-t a, .hint a, .la-list a, .doc-in a { text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 2px; }
.ev-prose a:hover, .note-t a:hover, .hint a:hover { text-decoration-color: currentColor; }
/* B-2 (Phase 13, D3): this redesign-pass rule re-paled the thumb to `--raise`
   — near-identical to `--bg`, i.e. the SAME invisible scrollbar the clip
   complained about, restated later in the cascade where it would win. The
   real rule is at the top of the file now (`--mid` on `--raise`); this
   section keeps nothing of its own for the scrollbar. */
.fade { animation: fade 150ms ease both; }

/* ---- 1. the shell (Linear / Perplexity): a white top bar with a small
   brand mark, an avatar row for the account, a thin left rail of icons +
   13px labels on the grey ground, and the content as a white sheet with
   an 8px radius. On a phone the rail is the existing bottom bar. ---- */
.topnav { background: var(--panel); border-bottom: 1px solid var(--line); padding: 0 20px; min-height: 48px; gap: 10px; }
.topnav .brand { font-size: var(--fs-3); font-weight: 600; letter-spacing: -.01em; text-shadow: none; }
.topnav .crumb { font-family: var(--font); font-size: var(--fs-2); color: var(--low); }
.topnav .crumb-domain { font-family: var(--font); font-size: var(--fs-2); color: var(--mid); }
.topnav .who, .topnav .signout { font-family: var(--font); font-size: var(--fs-2); }
.acct-menu > summary.who { font-family: var(--font); font-size: var(--fs-2); color: var(--mid); display: inline-flex; align-items: center; gap: 8px; padding: 4px 6px; border-radius: 8px; }
.acct-menu > summary.who:hover, .acct-menu[open] > summary.who { background: var(--panel2); color: var(--hi); }
.avatar { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 999px;
  background: var(--note-bg); color: var(--note); font-size: var(--fs-1); font-weight: 600; flex-shrink: 0; }
.acct-menu-b { background: var(--panel); border-radius: 8px; padding: 6px; min-width: 168px; }
.acct-menu-b a { font-size: var(--fs-2); color: var(--hi); border-radius: 6px; }
.acct-menu-b a:hover { background: var(--panel2); }
.acct-menu-name { font-size: var(--fs-2); }
.pshell { background: var(--bg); min-height: calc(100vh - 49px); }
/* Ben fold-in 2 (L11): the rail width lives ABOVE the phone breakpoint only —
   written bare it out-ranked the `max-width: 900px` reset (`width: auto`) at
   the top of the file and the fixed bottom nav ran 168px wide at 390, its
   top hairline stopping short. */
.pnav { padding: 12px 8px; border-right: 0; background: transparent; }
@media (min-width: 901px) { .pnav { width: 168px; } }
.pnav-brand { font-size: var(--fs-2); font-weight: 600; color: var(--hi); padding: 6px 10px 12px; overflow-wrap: anywhere; }
.pnav-item { padding: 7px 10px; border-radius: 6px; font-size: var(--fs-2); font-weight: 500; color: var(--mid); gap: 9px; }
.pnav-item:hover { background: var(--panel2); color: var(--hi); }
.pnav-item.on { background: var(--panel2); color: var(--hi); }
.pnav-ic { width: 16px; height: 16px; }
.pnav-item .n { font-family: var(--font); font-size: var(--fs-1); font-weight: 500; padding: 1px 6px; border-radius: 999px; background: var(--raise); color: var(--mid); }
.pnav-more > summary { font-size: var(--fs-2); border-radius: 6px; padding: 7px 10px; }
.pnav-more[open] > summary, .pnav-more > summary:hover { background: var(--panel2); }
.pnav-more-b a, .pnav-more-b button { font-size: var(--fs-2); border-radius: 6px; }
.pnav-more-b a:hover, .pnav-more-b button:hover:not(:disabled) { background: var(--panel2); }
.pmain { background: var(--panel); border: 1px solid var(--line); border-radius: 8px; margin: 12px 12px 12px 0; min-width: 0; }
.rail { background: var(--panel); border-right: 1px solid var(--line); }
.rail-item.on { background: var(--panel2); border-left-color: var(--hi); }
.rail-item .r1 .h, .rail-item .r2 { font-family: var(--font); }
.phead { padding: 20px 28px 16px; gap: 16px; }
.phead h1 { font-size: var(--fs-6); font-weight: 600; letter-spacing: -.02em; }
.phead .sub { font-family: var(--font); font-size: var(--fs-2); color: var(--low); margin-top: 4px; }
.phead .num .v { font-family: var(--font); font-size: var(--fs-5); font-weight: 600; }
.phead .num .l { font-family: var(--font); font-size: var(--fs-1); color: var(--low); }
.phead-note { font-family: var(--font); font-size: var(--fs-1); }
.tbody { padding: 24px 28px var(--fab-clear); }
@media (max-width: 900px) {
  .pmain { margin: 0; border: 0; border-radius: 0; }
  .pnav { background: var(--panel); padding: 4px 4px calc(4px + env(safe-area-inset-bottom)); }
  .pnav-item { font-size: 10px; color: var(--mid); border-radius: 8px; }
  .pnav-item.on { background: transparent; color: var(--hi); }
  .pnav-item .n { transform: translate(10px, -4px); }
  .phead { padding: 16px 16px 12px; }
  .tbody { padding: 16px 16px var(--fab-clear); }
  .topnav { padding: 0 14px; }
}

/* ---- 2. the shared atoms, re-drawn once (every reference agrees):
   card titles as small bold dark type, chips as tiny pills, ONE pale-green
   primary, ghost buttons white with a hairline, inputs white, 8px
   controls, 10px cards, 36px rows. ---- */
.label { font-family: var(--font); font-size: var(--fs-2); font-weight: 600; color: var(--hi); letter-spacing: 0; text-transform: none; margin-bottom: 6px; }
.label.quiet, .tlist-head .label, .tselside .label, .rail .label, .login-box .label { font-size: var(--fs-1); font-weight: 500; color: var(--low); text-transform: uppercase; letter-spacing: .06em; }
.chip { font-family: var(--font); font-size: var(--fs-1); font-weight: 500; line-height: 1.4; border-radius: 999px; padding: 2px 8px; border: 1px solid var(--line); color: var(--mid); background: var(--panel); }
.chip.filled { color: var(--hi); background: var(--raise); }
.chip.low-c { color: var(--low); background: var(--raise); border-color: transparent; }
.chip.sev-high, .chip.sev-critical { color: var(--crit); background: var(--crit-bg); border-color: transparent; }
.chip.sev-medium { color: var(--warn); background: var(--warn-bg); border-color: transparent; }
.chip.sev-low { color: var(--note); background: var(--note-bg); border-color: transparent; }
.chip.filled.sev-high, .chip.filled.sev-critical { background: var(--crit-bg); color: var(--crit); }
.chip.filled.sev-medium { background: var(--warn-bg); color: var(--warn); }
.chip.filled.sev-low { background: var(--note-bg); color: var(--note); }
/* the verdict pill on a change card and the "after" chip of a From -> To
   pair: classes, never an inline colour (project.html, Phase 6) */
.chip.v-good { color: var(--good); background: var(--good-bg); border-color: transparent; }
.chip.v-crit { color: var(--crit); background: var(--crit-bg); border-color: transparent; }
.chip.v-mid { color: var(--mid); background: var(--raise); border-color: transparent; }
.chip.fromto-c.after { background: var(--note-bg); color: var(--note); border-color: transparent; }
button.chip { border-radius: 8px; }
.btn-primary { font-family: var(--font); font-size: var(--fs-3); font-weight: 500; color: var(--accent-ink); background: var(--accent);
  border: 1px solid transparent; border-radius: 8px; padding: 8px 14px; line-height: 1.3; transition: filter var(--motion); }
.btn-primary:hover:not(:disabled) { filter: brightness(.96); }
.btn-primary[disabled] { opacity: .5; }
.btn-primary.ghost { background: var(--panel); color: var(--hi); border-color: var(--line); }
.btn-ghost { font-family: var(--font); font-size: var(--fs-3); font-weight: 500; color: var(--hi); background: var(--panel);
  border: 1px solid var(--line); border-radius: 8px; padding: 7px 12px; line-height: 1.3; }
.btn-ghost:hover:not(:disabled) { border-color: var(--mid); background: var(--panel); }
.btn-ghost.sm, .btn-sm { font-size: var(--fs-2); padding: 5px 10px; border-radius: 8px; }
.btn-off { font-family: var(--font); font-size: var(--fs-2); border-radius: 8px; padding: 5px 10px; color: var(--mid); background: var(--panel); }
.btn-link { font-size: var(--fs-2); }
input, select, textarea { background: var(--panel); color: var(--hi); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; font-family: var(--font); }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--mid); }
input::placeholder, textarea::placeholder { color: var(--low); }
.panel { border-radius: 10px; padding: 20px; background: var(--panel); }
.panel.flush { padding: 0; }
.note-t { font-size: var(--fs-2); color: var(--low); }
.ev-prose { font-size: var(--fs-2); color: var(--mid); }
.rep-t { font-size: var(--fs-5); font-weight: 600; letter-spacing: -.01em; }
.rep-sub { font-family: var(--font); font-size: var(--fs-2); }
.sevdot { width: 6px; height: 6px; }
.err { color: var(--crit); }
table.flat th { font-family: var(--font); font-size: var(--fs-1); font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--low); padding: 8px 8px; }
table.flat td { font-size: var(--fs-2); padding: 9px 8px; border-bottom: 1px solid var(--soft); }
table.flat td.mono, table.flat .mono { font-family: var(--font); font-size: var(--fs-2); }
.pbanner { border-radius: 10px; padding: 14px 16px; border-color: var(--line); background: var(--panel2); }
.pbanner-t { font-family: var(--font); font-size: var(--fs-3); font-weight: 600; }
.pbanner-b { font-size: var(--fs-2); }
.waitcard { border-color: var(--line); background: var(--panel); border-radius: 10px; padding: 32px 24px; }
.waitcard h2 { font-size: var(--fs-5); font-weight: 600; }
.waitcard p { font-size: var(--fs-3); }
.login-box { background: var(--panel); border-color: var(--line); }
.login-box h1 { font-size: var(--fs-5); }
.tabs a { font-family: var(--font); font-size: var(--fs-2); font-weight: 500; }
.tabs a.on { border-bottom-color: var(--hi); }
.tabs::after { background: linear-gradient(to right, transparent, var(--panel)); }

/* the segmented control (Buffer's Pay Monthly / Pay Yearly, Copilot's
   ranges): a pale track, the selected pill in the accent with dark text */
.rmap-filters, .plan-toggle { background: var(--raise); border-color: transparent; padding: 3px; gap: 2px; }
.rmap-filters .chip, .plan-toggle .chip { font-size: var(--fs-2); font-weight: 500; padding: 5px 12px; color: var(--mid); background: transparent; }
.rmap-filters .chip.on, .plan-toggle .chip.on { color: var(--accent-ink); background: var(--accent); box-shadow: none; }
.range { gap: 2px; background: var(--raise); border-radius: 999px; padding: 2px; }
.range a, .rng-custom { font-family: var(--font); font-size: var(--fs-1); font-weight: 500; color: var(--mid); padding: 3px 9px; border-radius: 999px; border: 0; }
.range a.on, .rng-custom.on { color: var(--hi); background: var(--panel); border: 0; box-shadow: var(--shadow); }
.range a .n { color: var(--low); }
.drange { background: var(--panel); border-bottom: 1px solid var(--line); padding: 10px 28px; }
.drange-lab, .drange-when, .chart-win { font-family: var(--font); font-size: var(--fs-1); letter-spacing: 0; text-transform: none; }
.rng-pop { background: var(--panel); }
.rng-pop label { font-family: var(--font); letter-spacing: 0; text-transform: none; font-size: var(--fs-1); }
.legend, .chart-key { font-family: var(--font); font-size: var(--fs-1); }
.chart-tip { background: var(--panel); }
.chart-tip .d, .chart-tip .r { font-family: var(--font); }

/* ---- 3. the roadmap (Plane density + Perplexity pills + Copilot bars):
   rows on hairlines, no card per row; the title 14px, one grey line
   13px; the two bars thin on a pale track with the estimate number at
   the end; the state as a tiny pale pill; the sticky bar white with a
   hairline and the ONE accent button. ---- */
.csplit { gap: 24px; }
.ipane { padding: 0; }
/* Checker catch (Phase 13 screenshot review, 19 Sep): B-1 says "check the
   last task row is reachable with the panel closed" — on a DESKTOP width it
   was not. S-6, further down this file, already reserves room for a phone's
   sticky bars on `#tlist` inside `@media (max-width: 640px)`; above that
   width `.ipane`/`.rmap-one` carry no bottom padding at all (`padding: 0`,
   above), so nothing on the Roadmap ever cleared a floating fab there — a
   CLIENT simply never had one to notice it against before this phase.
   `--fab-clear` is the token every other scrollable area already uses for
   exactly this; the more specific `#tlist` rule inside S-6's media block
   still wins on a phone, unchanged. */
.tlist { border: 0; border-radius: 0; border-top: 1px solid var(--line); background: transparent; overflow: visible; padding-bottom: var(--fab-clear); }
.tlist-head { margin: 8px 0 6px; }
.tlist-head .note-t { font-size: var(--fs-2); }
.impbars-legend { margin: 0 0 8px; font-size: var(--fs-1); }
.lsearch-i { font-family: var(--font); font-size: var(--fs-2); border-radius: 8px; padding: 6px 10px; background: var(--panel); }
.lsearch-n, .lsearch-x { font-family: var(--font); font-size: var(--fs-1); }
.irow { display: flex; align-items: center; gap: 20px; padding: 10px 12px; border-bottom: 1px solid var(--line); border-left: 0; }
.irow-main { flex: 1 1 auto; min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px; row-gap: 2px; align-items: center; }
.irow .t1 { grid-column: 1; }
.irow .t1 .sevdot { margin-top: 7px; }
.irow .title { font-size: var(--fs-3); font-weight: 500; line-height: 1.4; }
.irow .t2 { grid-column: 2; grid-row: 1; margin: 0; padding: 0; font-family: var(--font); font-size: var(--fs-1); color: var(--low); gap: 6px; flex-wrap: nowrap; }
.irow .t3 { grid-column: 1 / -1; margin: 0; padding-left: 14px; font-size: var(--fs-2); line-height: 1.45; }
/* Ben fold-in P8 round 2 (H-2 folded with U2-01, a second UAT's P1: "click
   sometimes only highlights/navigates the row without unmistakable
   expansion"). H-2 took the repeated title/chips out of the body, and that
   repeat used to be part of what told a client the click had done
   something, so the row itself now carries the signal instead: a stronger
   ring (not just the 2px inset bar) plus a chevron that flips. */
.irow.on, #tlist .irow[aria-expanded="true"] {
  background: var(--panel2); border-left: 0;
  box-shadow: inset 2px 0 0 var(--hi), inset 0 0 0 1px var(--hi);
}
.irow:hover { background: var(--panel2); }
.irow-chev {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  color: var(--low); transition: transform var(--motion), color var(--motion);
}
.irow.on .irow-chev, #tlist .irow[aria-expanded="true"] .irow-chev { color: var(--hi); transform: rotate(180deg); }
.irow .impbars { flex: 0 0 200px; flex-direction: column; flex-wrap: nowrap; gap: 5px; margin: 0; }
.impbar { min-width: 0; max-width: none; flex: none; gap: 8px; }
.impbar-l { font-family: var(--font); font-size: var(--fs-1); color: var(--low); width: 84px; }
.impbar-t { height: 6px; background: var(--raise); }
.impbar-n { font-size: var(--fs-1); color: var(--mid); width: 22px; text-align: right; flex-shrink: 0; font-variant-numeric: tabular-nums; }
.impkey { height: 6px; width: 14px; }
.irow-locked { border-bottom: 1px solid var(--line); }
.irow-locked .locked-blur { font-size: var(--fs-3); }
.tlist-locked::before { background: linear-gradient(to bottom, transparent, var(--panel) 85%); }
/* grouped by the roadmap's own buckets when the view is All (Plane's
   "Urgent 0 / High 2" headers, one count each) */
.rmap-group-h { display: flex; align-items: baseline; gap: 8px; padding: 12px 12px 6px; font-size: var(--fs-2); font-weight: 600; color: var(--hi); }
.rmap-group-h .n { font-weight: 500; color: var(--low); }
.rmap-group + .rmap-group { margin-top: 4px; }
.rmap-group .irow:last-child { border-bottom: 1px solid var(--line); }
/* Phase 18 (V5-07): the Tidy-ups group after the ranking work, its one line */
.rmap-tidy { margin-top: 14px; border-top: 1px solid var(--line); }
.rmap-tidy-l { margin: 0; padding: 0 12px 8px; }
/* Phase 18 (V5-01): Site health's one line and Search performance */
.hcard-means { margin: 2px 0 0; }
.hcard-perf { margin: 6px 0 0; }
/* Phase 18 (V5-06): pick 5 to 10 focus searches (signup step 2, add a site,
   Settings). Chips wrap; nothing scrolls sideways at 390px. */
.focus-pick { border: 0; padding: 0; margin: 0 0 16px; min-width: 0; }
.focus-pick legend { font-weight: 600; padding: 0; margin-bottom: 4px; }
.pick-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 10px; }
.pick-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 999px; font-weight: 400; cursor: pointer; max-width: 100%; }
.pick-chip input,
.fld.focus-pick .pick-chip input[type=checkbox] { margin: 0; width: auto; height: auto; padding: 0; flex: none; border: 0; }
.fld.focus-pick .pick-chip { display: inline-flex; width: auto; margin: 0; font-size: var(--fs-2); line-height: 1.3; }
.pick-chip:has(input:checked) { border-color: var(--hi); background: var(--panel2); }
.focus-list { list-style: none; padding: 0; margin: 8px 0; display: flex; flex-wrap: wrap; gap: 6px; }
/* Phase 18 (addendum #6): a demand badge's phrases, under its row */
.demand-x { margin: 0 0 6px 48px; font-size: var(--fs-2); color: var(--mid); }
.demand-x summary { cursor: pointer; }
.demand-x ul { margin: 4px 0 0; padding-left: 18px; }
.demand-x.lshide { display: none; }
.gtf-tidy { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--line); }
.gtf-tidy-h { margin: 0; }
.ai-pick { margin: 6px 0 0; position: relative; z-index: 1; }
.kwtable .kwhide-b { margin-left: 6px; font-size: var(--fs-1); color: var(--low); text-decoration: none; background: none; border: 0; padding: 0; cursor: pointer; }
.kwtable .kwhide-b:hover, .kwtable .kwhide-b:focus-visible { color: var(--mid); text-decoration: underline; }
.ask-inline { background: var(--panel); border-radius: 10px; }
.ask-inline summary { font-size: var(--fs-2); }
.cempty { padding: 24px 0; }
.ce-t { font-size: var(--fs-4); font-weight: 600; }
.ce-s { font-size: var(--fs-2); }
/* the sticky section bar */
.gtf-bar { background: var(--panel); border: 0; border-bottom: 1px solid var(--line); border-radius: 0; box-shadow: none; padding: 12px 28px; margin: 0; }
.gtf-bar-l { gap: 14px; }
.gtf-lead { font-size: var(--fs-2); }
.gtf-sent { font-size: var(--fs-2); }
.gtf-cta { padding: 8px 16px; font-size: var(--fs-3); }
.gtf-timeline li { font-family: var(--font); font-size: var(--fs-1); }
.gtf-timeline li.on .gtf-dot { background: var(--good); }
.gtf-timeline li + li::before { width: 16px; }
@media (max-width: 900px) { .gtf-bar { padding: 10px 16px; } }
/* the task panel (Linear's sheet): a white sheet, 8px radius, quiet
   headings, the URL list as a plain table */
.rpane { padding-right: 0; }
.rpane .idetail { border: 1px solid var(--line); border-radius: 8px; background: var(--panel); border-left: 1px solid var(--line); }
.idetail-h { padding: 16px 18px 14px; }
.idetail-h h3 { font-size: var(--fs-4); font-weight: 600; }
.idetail-b { padding: 16px 18px 16px; }   /* fix round M4: no --fab-clear */
.idetail-f { background: var(--panel); border-radius: 0 0 8px 8px; }
.ev-head { font-size: var(--fs-2); font-weight: 600; }
.ev-sub { font-size: var(--fs-1); }
.ev-plain { background: var(--panel2); border-left: 3px solid var(--note); border-radius: 8px; }
.ev-plain-h { font-size: var(--fs-3); }
.ev-pages a { font-family: var(--font-mono); font-size: var(--fs-1); }
.ev-table { font-size: var(--fs-2); }
.ev-table td { padding: 7px 10px 7px 0; }
.ev-table-n { color: var(--low); }
.ev-facts th { font-size: var(--fs-1); }
.ev-facts td { font-family: var(--font); font-size: var(--fs-2); font-variant-numeric: tabular-nums; }
.act-card { border-radius: 8px; padding: 12px; }
.act-card-primary { border-color: var(--hi); }
.act-ribbon { border-radius: 999px; font-size: var(--fs-1); letter-spacing: 0; text-transform: none; padding: 1px 8px; }
.tech-detail > summary { font-family: var(--font); font-size: var(--fs-2); }
.panel-kbd-hint { font-size: var(--fs-1); }
.mock-note { background: var(--panel2); border-radius: 8px; }
.ev-retired, .ev-closed, .ev-intended { border-radius: 8px; }
.decide { background: var(--panel2); border-left-color: var(--note); }
.decide .opt { background: var(--panel); border-radius: 8px; }
.decide .opt:has(input:checked) { border-color: var(--hi); background: var(--panel2); }
.opt-c { font-family: var(--font); letter-spacing: 0; text-transform: none; font-size: var(--fs-1); }
.rec-badge { font-family: var(--font); letter-spacing: 0; text-transform: none; border-radius: 999px; background: var(--note-bg); border-color: transparent; padding: 1px 7px; }
.rec-basis { background: var(--panel); border-radius: 8px; }
.audit-mini { margin-top: 24px; }
/* the staff selection bar and side box float, so they carry the shadow */
.tselbar { background: var(--panel); font-size: var(--fs-2); }
.tselside { background: var(--panel); }
.tselside-row { font-size: var(--fs-2); }
.tselside-row b { font-family: var(--font); }
@media (max-width: 760px) {
  .irow { flex-direction: column; align-items: stretch; gap: 8px; padding: 10px 12px; }
  .irow-main { grid-template-columns: minmax(0, 1fr); }
  .irow .t2 { grid-column: 1; grid-row: auto; padding-left: 14px; margin-top: 2px; flex-wrap: wrap; }
  .irow .t3 { grid-column: 1; }
  .irow .impbars { flex: none; padding-left: 14px; }
}

/* ---- 4. the fix-request sheet + its confirmation (Hers): a side sheet
   from the right; "Get these fixed", the locked copy, a plain task list
   with checkboxes; after the send the same shape says "Request received"
   with the locked sentence and a 3-step vertical timeline. ---- */
.modal.sheet { justify-content: flex-end; align-items: stretch; padding: 0; }
.modal.sheet .modal-card { max-width: 480px; max-height: none; height: 100%; border-radius: 0; border: 0; border-left: 1px solid var(--line);
  animation: slidein 150ms ease both; }
@keyframes slidein { from { opacity: 0; transform: translateX(12px); } to { opacity: 1; transform: none; } }
.modal-card { background: var(--panel); border-radius: 8px; box-shadow: var(--shadow); animation: fade 150ms ease both; }
.modal-h { padding: 18px 20px 14px; border-bottom: 0; }
.modal-h h3 { font-size: var(--fs-5); font-weight: 600; }
.modal-x { font-size: 22px; color: var(--mid); }
.modal-b { padding: 4px 20px 16px; }
.modal-f { padding: 14px 20px; border-top: 1px solid var(--line); }
.gtf-count { margin: 6px 0 12px; }
.gtf-list { border: 0; border-top: 1px solid var(--line); border-radius: 0; }
.gtf-row { padding: 9px 4px; border-bottom: 1px solid var(--line); gap: 12px; }
.gtf-row input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--hi); margin: 0; }
.gtf-row-t { font-size: var(--fs-3); }
.gtf-row-m { font-family: var(--font); font-size: var(--fs-1); }
.gtf-done-lead { font-size: var(--fs-3); color: var(--hi); margin: 0 0 20px; line-height: 1.5; }
.gtf-steps { list-style: none; margin: 0; padding: 0; position: relative; }
.gtf-steps::before { content: ''; position: absolute; left: 9px; top: 12px; bottom: 12px; width: 1px; border-left: 1px dashed var(--line); }
.gtf-step { position: relative; display: flex; gap: 14px; align-items: flex-start; padding: 0 0 22px; }
.gtf-step:last-child { padding-bottom: 0; }
.gtf-step-dot { position: relative; z-index: 1; width: 19px; height: 19px; border-radius: 999px; flex-shrink: 0; margin-top: 1px;
  border: 1.5px solid var(--line); background: var(--panel); display: flex; align-items: center; justify-content: center; font-size: 11px; }
.gtf-step.done .gtf-step-dot { border-color: var(--good); background: var(--good); color: var(--panel); }
.gtf-step.done .gtf-step-dot::after { content: '✓'; }
.gtf-step.now .gtf-step-dot { border-color: var(--warn); }
.gtf-step.now .gtf-step-dot::after { content: ''; width: 7px; height: 7px; border-radius: 999px; background: var(--warn); }
.gtf-step-t { font-size: var(--fs-3); font-weight: 500; color: var(--hi); line-height: 1.4; }
.gtf-step.todo .gtf-step-t { color: var(--mid); font-weight: 400; }
.gtf-step-s { font-size: var(--fs-2); color: var(--low); margin-top: 2px; }
.toast { background: var(--panel); border-color: var(--line); border-radius: 10px; padding: 10px 12px 10px 16px; gap: 10px;
  left: 24px; bottom: 24px; top: auto; transform: none; max-width: min(560px, calc(100% - 48px)); }
.toast.gone { transform: translateY(8px); }
@media (max-width: 900px) { .toast { left: 12px; right: 12px; top: 12px; bottom: auto; max-width: none; } .toast.gone { transform: translateY(-8px); } }
.toast.good { border-color: var(--line); }
.toast.good::before { content: ''; width: 8px; height: 8px; border-radius: 999px; background: var(--good); flex-shrink: 0; }
.toast-t { font-size: var(--fs-2); font-weight: 500; }
.toast-s { font-family: var(--font); font-size: var(--fs-1); }
@media (max-width: 620px) { .modal.sheet .modal-card { max-width: none; border-left: 0; } }

/* ---- 5. the live audit (Sentry card + Copilot + Tana): a hero card with
   the site name, last / next audit as two labelled stats, the score
   sparkline as small grey bars with the latest in green, the pages-read
   sentence as the grey line; what changed as a day-grouped plain list
   with tiny Opened / Verified pills. ---- */
.la-hero-h { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.la-site { font-size: var(--fs-5); font-weight: 600; letter-spacing: -.01em; }
.la-live { width: 7px; height: 7px; margin: 0 4px 0 0; box-shadow: 0 0 0 3px var(--good-bg); }
.la-hero-sub { font-size: var(--fs-2); color: var(--mid); margin: 4px 0 0; }
.la-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px 32px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line); }
.la-stat .label { font-size: var(--fs-1); font-weight: 500; color: var(--low); margin-bottom: 4px; }
.la-big { font-family: var(--font); font-size: var(--fs-4); font-weight: 600; color: var(--hi); margin: 0 0 4px; line-height: 1.35; }
.la-card { background: var(--panel); border-radius: 10px; padding: 16px 18px; }
.la-grid { margin-top: 16px; }
.la-trend { height: 48px; gap: 3px; border-bottom: 1px solid var(--line); margin-top: 12px; }
.la-trend span { background: var(--raise); border-radius: 2px 2px 0 0; min-width: 5px; max-width: 8px; }
.la-trend span:last-child { background: var(--good); }
.la-day { font-size: var(--fs-1); font-weight: 500; color: var(--low); margin: 12px 0 2px; }
.la-list { padding-left: 0; font-size: var(--fs-2); }
.la-list::before, .la-list li::before { content: none; }
.la-list li { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--soft); }
.la-list li:last-child { border-bottom: 0; }
.la-list li.note-t { border-bottom: 0; }
.la-list a { color: var(--hi); text-decoration: none; min-width: 0; overflow-wrap: anywhere; }
.la-pill { font-size: var(--fs-1); font-weight: 500; border-radius: 999px; padding: 1px 8px; white-space: nowrap; flex-shrink: 0; }
.la-pill.opened { color: var(--note); background: var(--note-bg); }
.la-pill.verified { color: var(--good); background: var(--good-bg); }
.la-sub { margin: 14px 0 0; }
.la-empty { text-align: center; padding: 28px 20px; border: 1px dashed var(--line); border-radius: 10px; margin-top: 16px; }
.la-empty .la-big { margin-bottom: 6px; }
.plan-gate { border-color: var(--line); background: var(--panel2); border-radius: 10px; }

/* ---- 6. insights (Copilot Money): white cards two across, one number
   each, thin charts, the range as a pill control ---- */
.tiles { gap: 16px; }
.tile { background: var(--panel); border-radius: 10px; padding: 16px 18px; flex: 1 1 calc(50% - 8px); }
.tile .k, .tile .tl { font-family: var(--font); font-size: var(--fs-2); font-weight: 600; color: var(--hi); letter-spacing: 0; text-transform: none; }
.tile .v, .tile .tv { font-family: var(--font); font-size: var(--fs-6); font-weight: 600; letter-spacing: -.02em; margin-top: 8px; }
.tile .d, .tile .td, .tile .src, .tile .tp { font-family: var(--font); font-size: var(--fs-1); }
.gcard { border-radius: 10px; background: var(--panel); }
.gcard-t { font-size: var(--fs-3); }
.gcard-chips { color: var(--mid); font-size: var(--fs-1); }
.gcard.pri { border-color: var(--line); }
.gcard-cta { background: var(--panel2); }
.gsec { font-size: var(--fs-4); font-weight: 600; }
.gsec-s { font-size: var(--fs-2); }
.results-summary { font-size: var(--fs-3); }
.osec-t { font-size: var(--fs-3); }
.osec-n { font-family: var(--font); }
.pp-l, .pp-v, .bucket-l, .bucket-v { font-family: var(--font); font-size: var(--fs-1); text-transform: none; letter-spacing: 0; }
.pp-track, .bucket-track { background: var(--raise); }
.outcome-strip { background: var(--raise); }
.ov-pricard { background: var(--panel); border-radius: 10px; }
.ov-pri-n { font-family: var(--font); font-size: var(--fs-6); font-weight: 600; }
.ov-ghost-chart span { background: var(--raise); }
.ov-ghost-cta p { font-size: var(--fs-2); }
/* Phase 11, A-2: the score card's typography went with the card. */
.home-rail { background: var(--panel); border-radius: 10px; padding: 16px; }
.skel-bars span { background: var(--raise); }
.ask { border-radius: 10px; background: var(--panel); }
.ask-row textarea, .cform textarea { background: var(--panel); border-radius: 8px; }
.cpane { border-radius: 10px; }
.mhead .who, .mhead .when, .bmeta, .cnote, .mopen, .shown-tag { font-family: var(--font); letter-spacing: 0; text-transform: none; }
.bub { border-radius: 8px; font-size: var(--fs-2); }
.msg.you .bub { background: var(--panel2); }
.composer { background: var(--panel); }
.rempty { border-radius: 10px; }
.kwbar, .kwfilter { background: var(--panel2); border-radius: 8px; }
.kwf-l, .kwf-i, .kwf-m, .kwf-n { font-family: var(--font); text-transform: none; letter-spacing: 0; }
.mv-d, .mv-mu, .mv-n, .mv-tag, .cz-when, .cz-lag, .cz-eff, .cz-1 .src, .kwi-l, .kwi-p, .kwi-n { font-family: var(--font); }
.vp { font-family: var(--font); border-radius: 999px; }
.vp-good { color: var(--good); background: var(--good-bg); border-color: transparent; }
.vp-crit { color: var(--crit); background: var(--crit-bg); border-color: transparent; }
.vp-warn { color: var(--warn); background: var(--warn-bg); border-color: transparent; }
.round { font-family: var(--font); background: var(--raise); border-color: transparent; }
.pcard { border-radius: 10px; background: var(--panel); }
.pcard-top { font-family: var(--font); letter-spacing: 0; text-transform: none; color: var(--mid); }
.stick-table thead th { background: var(--panel); }

/* ---- 7. settings (Buffer's rail + Perplexity's connectors): a left
   sub-nav list; a connection as one row — logo, name, one grey line, a
   ghost "Connect" or "Connected · property ⋮"; billing as the Buffer card
   set with a requests table under it ---- */
/* G1 (fix round 2): the rail width lives ABOVE the phone breakpoint only —
   written bare it out-ranked the `max-width: 900px` reset at the top of the
   file (same specificity, later in source) and clipped the four items to
   168px at 390. */
@media (min-width: 901px) { .settings-subnav { width: 168px; gap: 2px; } }
.settings-subnav a { font-size: var(--fs-2); padding: 7px 10px; border-radius: 6px; }
.settings-subnav a.on { background: var(--panel2); color: var(--hi); font-weight: 600; }
.settings-subnav a:hover { background: var(--panel2); }
.conn-group { font-family: var(--font); font-size: var(--fs-1); font-weight: 500; letter-spacing: 0; text-transform: none; padding: 14px 18px 6px; }
.connrow { padding: 14px 18px; border-top: 1px solid var(--line); gap: 14px; align-items: flex-start; }
.connrow .right { padding-top: 2px; }
.connrow .nm { font-size: var(--fs-3); font-weight: 500; }
.connrow .dt { font-size: var(--fs-2); color: var(--mid); margin-top: 3px; }
.connrow .right { font-family: var(--font); font-size: var(--fs-1); gap: 8px; }
.conn-logo { color: var(--mid); }
.conn-prop { font-family: var(--font); font-size: var(--fs-2); color: var(--mid) !important; }
.conn-more { position: relative; }
.conn-more > summary { list-style: none; cursor: pointer; width: 28px; height: 28px; border-radius: 6px; border: 1px solid var(--line);
  display: inline-flex; align-items: center; justify-content: center; font-size: 16px; color: var(--mid); line-height: 1; background: var(--panel); }
.conn-more > summary::-webkit-details-marker { display: none; }
.conn-more > summary:hover, .conn-more[open] > summary { background: var(--panel2); color: var(--hi); }
.conn-more-b { position: absolute; right: 0; top: calc(100% + 4px); z-index: 20; min-width: 150px; padding: 6px; display: flex; flex-direction: column; gap: 2px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px; box-shadow: var(--shadow); }
.conn-more-b form { margin: 0; }
.conn-more-b button { width: 100%; text-align: left; border: 0; background: transparent; border-radius: 6px; padding: 7px 9px; font-size: var(--fs-2); color: var(--hi); cursor: pointer; }
.conn-more-b button:hover { background: var(--panel2); }
.conn-more-b .btn-off:hover { color: var(--crit); background: var(--crit-bg); }
.setup-ask .note-t { font-size: var(--fs-2); }
ol.howto li { font-size: var(--fs-2); }
ol.howto li::marker { font-family: var(--font); }
.conn-foot { font-size: var(--fs-2); }
.grec-lead { font-size: var(--fs-3); }
.grec-steps { font-size: var(--fs-2); }
/* billing: Buffer's plan cards */
.plan-toggle { margin: 4px 0 12px; }
.plan-cadence-note { font-size: var(--fs-2); }
.plan-grid { gap: 16px; max-width: 640px; }
.plan-card { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 20px; }
.plan-card-current { border: 2px solid var(--hi); padding: 19px; }
.plan-name { font-family: var(--font); font-size: var(--fs-4); font-weight: 600; }
.plan-price { font-family: var(--font); font-size: var(--fs-7); font-weight: 600; letter-spacing: -.02em; margin: 6px 0 12px; line-height: 1.2; }
.plan-price .plan-per, .plan-price .plan-save { font-family: var(--font); font-size: var(--fs-2); font-weight: 400; color: var(--low); letter-spacing: 0; }
.plan-price .plan-save { color: var(--good); }
.plan-price.plan-pending { font-family: var(--font); font-size: var(--fs-2); font-weight: 400; letter-spacing: 0; }
.plan-includes { list-style: none; padding: 0; margin: 16px 0 0; font-size: var(--fs-2); color: var(--mid); line-height: 1.7; }
.plan-includes li { padding-left: 20px; position: relative; }
.plan-includes li::before { content: '✓'; position: absolute; left: 0; color: var(--good); font-size: var(--fs-1); top: 1px; }
.plan-card .btn-primary { margin-top: 0; }
.plan-card form { margin: 0; }
.plan-foot { font-size: var(--fs-2); margin-top: 14px; }
.creditbar { background: var(--panel); border-radius: 10px; }
.creditnum { font-family: var(--font); font-weight: 600; }
.paircode { font-family: var(--font-mono); background: var(--panel2); border-color: var(--line); }
/* the account form */
.fld > label { font-size: var(--fs-2); font-weight: 600; margin-bottom: 6px; }
.hint { font-size: var(--fs-2); color: var(--low); }

/* ---- 8. the staff screens: inherit the tokens, nothing redrawn beyond
   the shared atoms above; the portfolio card and the attention list just
   read the new surfaces ---- */
.pcard .dm, .pcard .health, .pcard .health-l, .pcard .meta, .sevkey, .attn-row .pj, .logrow .t, .logrow .actor, .logrow .txt,
.qrow .t, .qrow .s, .ilist-split, .imp-name, .tile .foot, .wrow, .frame-lab, .sumline, .dayhead, .chg .url, .chg .eff,
.pchip, .advice-chip, .needs-call, .mv-tag, .kwf-i, .lsearch-i, .cz-num, .rec-badge, .opt-c, .ifilter a, .apr-n, .brief-text { font-family: var(--font); }
.brief-text, .ev-pages a, .paircode, .mono { font-family: var(--font-mono); }
.hero-h { font-size: var(--fs-6); font-weight: 600; }
.hero-sub { font-size: var(--fs-2); }
.pcard { background: var(--panel); }
.pcard .health { font-weight: 600; }
.attn { border-radius: 10px; background: var(--panel); }
.attn-row:hover { background: var(--panel2); }
.ifilter a.on { background: var(--raise); }
.mv[open] { background: var(--panel2); }
.mv-h:hover { background: var(--panel2); }
.doc-quote { background: var(--panel2); }
/* the phone's fixed section bar floats over the list, so it carries the
   hairline on top and the one shadow */
@media (max-width: 900px) { .gtf-bar.gtf-sticky { border-top: 1px solid var(--line); border-bottom: 0; box-shadow: var(--shadow); } }
.conn-main { min-width: 240px; max-width: 560px; flex: 1 1 320px; }
.la-hero-empty { border-style: dashed; }
/* the account form (Buffer's create-account column): a label over each
   white input, one column */
.fld > label { display: block; }
.fld input:not([type=checkbox]):not([type=radio]) { width: 100%; max-width: 420px; }

/* ================================================================
   Fix round 2 (checker findings on Phase 6, 2026-09-14). Colours stay in
   :root; these re-draw shapes only. ---------------------------------- */

/* G2 / G3 — the row grid. The words column keeps a real minimum, the bars
   shrink first, and under 560px of LIST width (the owner's list beside the
   projects rail measures 460–503px; the client's at 1440 measures 615px, so
   the brief's "~620" would have stacked the client too) the bars stack
   UNDER the title and the pills drop under it. `.tlist` is the query
   container — the fixed selection bar (`.tselbar`) sits outside it in
   project.html, so containment re-parents nothing. The row rhythm is
   tightened with it (8px padding, no row gap, 1.35 line-height) so a
   two-line title still lands the row at ~72px. */
.tlist { container-type: inline-size; }
.irow { padding: 8px 12px; }
.irow-main { grid-template-columns: minmax(220px, 1fr) auto; row-gap: 0; }
.irow .title { line-height: 1.35; }
.irow .t3 { line-height: 1.35; }
.irow .impbars { flex: 0 0 clamp(140px, 38%, 224px); }
/* the one grey line: the why, clamped, with "· N pages" kept at its end
   (the count used to be a third pill on the title line) */
.irow .t3.t3-row { display: flex; align-items: baseline; gap: 6px; overflow: visible; }
/* Phase 7 (RM-11): the why line is clamped to TWO lines at every width — this
   is the ONE clamp rule for it (the four competing ones are gone); the cut is
   the browser's (whole words), never a server slice, and the full why sits
   in the accordion body. */
.irow .t3-why { flex: 1 1 auto; min-width: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.irow .t3-pages { flex-shrink: 0; white-space: nowrap; }
@container (max-width: 560px) {
  .irow { flex-direction: column; align-items: stretch; gap: 8px; }
  .irow-main { grid-template-columns: minmax(160px, 1fr); }
  .irow .t2 { grid-column: 1; grid-row: auto; padding-left: 14px; margin-top: 2px; flex-wrap: wrap; }
  .irow .impbars { flex: none; padding-left: 14px; }
}
@media (max-width: 760px) {
  .irow-main { grid-template-columns: minmax(0, 1fr); }
}

/* G5 → superseded by Ben fold-in 2 L9: at phone width the toast is dropped
   when the received sheet is open (the sheet is the signal); see the toast
   script in project.html. Desktop keeps the bottom-left toast. */

/* G6 — a filled chip with a status word is the tint + its ink (≥ 4.5:1),
   never a status colour as the ground under dark text */
.chip.filled.warn-c { background: var(--warn-bg); color: var(--warn); }
.chip.filled.good-c { background: var(--good-bg); color: var(--good); }
.chip.filled.crit-c { background: var(--crit-bg); color: var(--crit); }
.chip.filled.low-c { background: var(--raise); color: var(--low); }

/* G7 — a Slack channel name is a link-coloured word, never the accent fill */
.slack-name { color: var(--note); }
.note-c { color: var(--note); }
/* the task panel's footer state line ("Done …", "In progress since …"):
   the sans face, 13px, tone by class */
.foot-state { font-size: var(--fs-2); }

/* G12 — client-facing labels in the sans face at 13px; mono stays only for
   a literal code (the pairing code) */
.ev-table th { text-align: left; font-family: var(--font); font-size: var(--fs-2); font-weight: 600; color: var(--mid); }
.prop-label { font-family: var(--font); font-size: var(--fs-2); }

/* ================================================================
   Ben fold-in 2 (BEN_FOLD_IN_2.md, 2026-09-14). Colours stay in :root;
   shapes only. ---------------------------------------------------- */

/* H1 — the staff action row: one full-width line under the title, its
   controls wrapping as units (the why box shrinks first, the buttons never
   clip); the Send button keeps its ≤ 900px hide (the sticky bar). */
.idetail-acts { flex-basis: 100%; min-width: 0; align-items: center; }
.idetail-acts > * { max-width: 100%; min-width: 0; }
.idetail-acts .btn-primary { flex: none; white-space: nowrap; }
.idetail-dismiss { flex: 1 1 240px; min-width: 0; display: flex; gap: 8px; align-items: center; margin: 0; }
.idetail-dismiss input[type="text"] { flex: 1 1 120px; min-width: 0; font-size: var(--fs-2); }
.idetail-dismiss .btn-ghost { flex: none; white-space: nowrap; }

/* M3 — the client's bar tracks: a 64px label ("Google" / "AI" at 12px, the
   full name in the tooltip and the legend), a 24px right-aligned number,
   6px gaps, the bars column a fixed clamp(140px, 38%, 224px) that never
   shrinks (flex-shrink 0 — it used to give way to the title's max-content
   width, which is what left the track at 5–63px), and the words column may
   go down to 160px beside the pills. */
.irow { gap: 16px; }
.irow .impbar { gap: 6px; }
.irow .impbar-l { width: 64px; font-size: 12px; }
.irow .impbar-n { width: 24px; }
.irow-main { grid-template-columns: minmax(220px, 1fr) auto; }
.irow .title { overflow-wrap: normal; word-break: normal; }
/* L7 — the search volume on the grey line, one unit */
.irow .t3-vol { flex-shrink: 0; white-space: nowrap; }

/* M2 — the narrow (owner) list, stacked: pills back on the title line when
   the list is ≥ 440px, the two bars 5px on one row each with a 2px gap, the
   grey line one line, 6px 12px padding. */
@container (max-width: 560px) {
  .irow { flex-direction: column; align-items: stretch; padding: 6px 12px; gap: 3px; }
  .irow .title { line-height: 1.3; }
  .irow .impbars { flex: none; padding-left: 14px; gap: 2px; }
  .irow .impbar { line-height: 1; }
  .irow .impbar-l, .irow .impbar-n { line-height: 1; }
  .irow .impbar-t { height: 5px; }
}
@container (min-width: 440px) and (max-width: 560px) {
  .irow-main { grid-template-columns: minmax(160px, 1fr) auto; }
  .irow .t2 { grid-column: 2; grid-row: 1; padding-left: 0; margin-top: 0; flex-wrap: nowrap; }
}

/* M4 — the not-matched connection as the same quiet row: the sub-block
   spans the row under the name and the pill, 13px steps, the picker as the
   one ghost button. */
.connrow-narrow .right { margin-left: auto; }
.connrow .conn-sub { flex-basis: 100%; margin-left: 0; padding-left: 14px; }
.conn-sub .grec-steps { margin: 4px 0 10px; font-size: var(--fs-2); line-height: 1.6; }
.conn-sub .grec-steps li + li { margin-top: 3px; }
.grec-note { margin: 8px 0 0; max-width: 560px; }

/* M6 — the being-checked Insights skeleton: the range pills, then the 2×2
   cards; the F3 sentence in the first card in flow, not as an overlay. */
.ins-range { display: inline-flex; margin: 0 0 14px; }
.ins-skel { margin: 0; }
.ins-skel .tile { flex: 1 1 calc(50% - 8px); min-width: 0; }
.ins-skel .tile.ov-ghost-wrap { overflow: visible; }
.ins-skel .ov-ghost-cta { position: static; inset: auto; display: block; padding: 0; margin-top: 8px; text-align: left; background: none; }
.ins-skel .ov-ghost-cta p { max-width: none; font-size: var(--fs-2); color: var(--mid); }

/* Visual QA, 19 Sep 2026: the two links under the sign-in form were 15px tall
   on a phone. 44px is the house floor for anything tappable. */
@media (max-width: 760px) {
  .tap-link { display: inline-flex; align-items: center; min-height: 44px; }
}

/* Phase 13 visual QA: a `.tbody` that is followed by more of the page clears
   nothing; the floating-button clearance belongs to the page's LAST block. */
.tbody.tbody-top { padding-bottom: 0; }

/* L11 — the client's phone pages: no dead band under the last card (the
   client has no floating buttons to clear; the nav clearance stays on
   `.pmain`). */
@media (max-width: 760px) {
  body.no-fab .tbody { padding-bottom: 16px; }
  body.no-fab .tbody > .panel:last-of-type { margin-bottom: 0; }
}

/* the phone one-column rule stays the LAST word on the row grid */
@media (max-width: 760px) {
  .irow-main { grid-template-columns: minmax(0, 1fr); }
}


/* ================================================================
   Phase 7 (PHASE7_BRIEF.md): behaviour + structure + copy. Colours stay in
   :root; these draw the accordion, the site switcher, the status row, the
   sort, the What-to-do-next row, the support and add-site forms. ---- */

/* RM-03 — one column, the detail under its row (the right pane is gone) */
.rmap-one { display: block; }
.rmap-one .ipane { padding: 0; }
.rmap-aside { margin: 0 0 16px; }
.rmap-notice { padding: 10px 14px; margin: 0 0 12px; font-size: var(--fs-2); }
.acc-body { margin: 0 0 4px; border-radius: 8px; border: 1px solid var(--line); background: var(--panel); }
.acc-body .idetail-b { padding-bottom: 18px; }
.acc-foot { padding: 0 18px 16px; display: flex; justify-content: flex-end; }
.acc-collapse { text-decoration: none; }
#tlist .irow[aria-expanded="true"] { background: var(--panel2); }
/* Ben fold-in P8 round 2 (U2-01): a click-inserted body (roadmap.js) fades
   and drops in rather than appearing instantly, so the expand itself is
   part of the "it opened" signal alongside the row's ring and chevron. A
   server-rendered body (`?sel=` on load, JS-off) never carries `.acc-enter`
   at all, so it is simply visible — no animation to skip, nothing to wait
   on when there is no JS to add the class in the first place. */
.acc-body.acc-enter { opacity: 0; transform: translateY(-6px); }
.acc-body.acc-enter-active {
  opacity: 1; transform: translateY(0);
  transition: opacity .18s ease, transform .18s ease;
}
@media (prefers-reduced-motion: reduce) {
  .acc-body.acc-enter, .acc-body.acc-enter-active { opacity: 1; transform: none; transition: none; }
}
.idetail-meta { margin: 6px 0 0; }
.idetail-stamps { color: var(--low); }
.ev-steps { margin: 6px 0 0; padding-left: 20px; font-size: var(--fs-2); line-height: 1.6; color: var(--hi); }
.ev-steps li + li { margin-top: 4px; }

/* RM-03 / RM-10 — What to do next as a full-width row. Phase 11, A-2: it used
   to sit under the score card; the card is gone and this is the whole of the
   Roadmap's own body chrome above the list. */
.home-next { border: 1px solid var(--line); border-radius: 8px; padding: 14px 16px; background: var(--panel); margin: 12px 0 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 16px; align-items: center; }
.home-next .label { grid-column: 1 / -1; }
.home-next-t { margin: 0; font-size: var(--fs-3); font-weight: 600; color: var(--hi); }
.home-next-why { margin: 0; grid-column: 1; }
.home-next-cta { grid-column: 2; grid-row: 2 / span 2; white-space: nowrap; }
@media (max-width: 760px) { .home-next { grid-template-columns: 1fr; } .home-next-cta { grid-column: 1; grid-row: auto; text-align: center; } }

/* RM-02 — the site switcher in the top bar */
.site-menu { position: relative; display: inline-block; }
.site-menu > summary { cursor: pointer; list-style: none; padding: 4px 6px; border-radius: 6px; }
.site-menu > summary::-webkit-details-marker { display: none; }
.site-menu > summary:hover, .site-menu[open] > summary { background: var(--panel2); color: var(--hi); }
.site-menu-b { position: absolute; top: 100%; left: 0; z-index: 21; margin-top: 4px; min-width: 240px; background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 6px; display: flex; flex-direction: column; gap: 2px; box-shadow: var(--shadow); }
.site-menu-i { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0 10px; padding: 7px 9px; border-radius: 6px; color: var(--hi); font-size: var(--fs-2); text-decoration: none; }
.site-menu-i:hover, .site-menu-i.on { background: var(--panel2); }
.site-menu-n { font-weight: 600; }
.site-menu-d { grid-column: 1; font-size: var(--fs-1); color: var(--low); }
.site-menu-tick { grid-column: 2; grid-row: 1 / span 2; align-self: center; color: var(--good); }
.site-menu-add { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 9px; color: var(--note); border-radius: 0 0 6px 6px; }
.your-sites li { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.site-card .meta { display: flex; justify-content: space-between; margin-top: 12px; }

/* RM-08 — the status row + the sort */
.rmap-status-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: -4px 0 10px; }
.rmap-status { display: flex; gap: 6px; flex-wrap: wrap; }
.rmap-st-pill { font-size: var(--fs-1); padding: 3px 9px; text-decoration: none; }
.rmap-st-pill .n { font-weight: 500; color: var(--low); margin-left: 2px; }
.rmap-st-pill.on { background: var(--panel2); color: var(--hi); box-shadow: inset 0 0 0 1px var(--line); }
.rmap-st-pill.is-off { opacity: .5; cursor: default; }
.rmap-sort { display: inline-flex; align-items: center; gap: 6px; }
.rmap-sort select { font: inherit; font-size: var(--fs-2); padding: 4px 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); color: var(--hi); }
.btn-link.lnk { background: none; border: 0; padding: 0; color: var(--note); cursor: pointer; font: inherit; text-decoration: underline; text-decoration-color: var(--line); }

/* RM-07 — the long bars in the detail and the zero state */
.impbars-long { flex-direction: column; gap: 6px; margin: 10px 0 0; max-width: 520px; }
.impbars-long .impbar { max-width: none; }
.impbar-l-long { width: 190px; white-space: nowrap; font-family: var(--font); font-size: var(--fs-2); color: var(--mid); }
.impbars-long .impbar-n { width: 54px; font-size: var(--fs-2); color: var(--hi); }
.impbar-zero .impbar-t { background: var(--raise); opacity: .55; }
.impbar-none { width: auto !important; white-space: nowrap; color: var(--low); font-size: var(--fs-1); }
.irow .impbar-none { font-size: 11px; }

/* P1-4 — the connection return toast, warn tone */
.toast.warn { border-color: var(--warn); }

/* P0-2 — support */
.support-page .flat td { vertical-align: top; }
.support-offer { margin-top: 18px; }
.support-offer > summary { cursor: pointer; }
@media (max-width: 760px) {
  .rmap-status-row { align-items: flex-start; }
  .impbar-l-long { width: 150px; white-space: normal; }
}
.gtf-next { display: block; flex-basis: 100%; font-size: var(--fs-1); margin-top: 2px; }
/* Ben P14 round, finding 2 (24 Sep): this used to hide the holding/next-step
   line on the phone sticky bar entirely — a client on 390px never saw "We're
   on it. You'll hear from us soon." next to their own stale request, only on
   desktop. `.gtf-timeline` (the Requested/In progress/Done dots, hidden at
   this width already, project.html inline style) is the thing that was
   actually too tall for the bar; this one-line sentence is not. `flex-basis:
   100%` above forces it onto its own row under `.gtf-sent` inside the flex
   `.gtf-bar-l` rather than fighting the CTA button for horizontal room, so it
   never crowds the bar — the bar's own height is measured live by the script
   beside `.gtf-sticky` (project.html) and the page's bottom padding already
   follows it, so a taller bar here costs nothing else. */

/* Ben fold-in (B-7): "being checked" is plain grey text — never a pill — on the
   Connections row and the Insights Clicks card. */
.connrow .right .conn-being-checked, .conn-being-checked { display: inline; border: 0; background: none; padding: 0; margin: 0; border-radius: 0; box-shadow: none; font-family: var(--font); font-size: var(--fs-1); color: var(--low); }

/* ================================================================
   Phase 8 Part A (PHASE8_BRIEF.md, 2026-09-16): the Roadmap's visual
   layer, matched to the nine references in reviews/mobbin_refs_v2/
   (reviews/MOBBIN_ROADMAP_REFS.md). Shapes and spacing only — every
   colour is a token from :root, there is one shadow and one motion,
   and nothing here changes a word, a route or a gate.
   ---------------------------------------------------------------- */

/* ---- A1 (Todoist #2 list row): the row opens with a CIRCLE state mark
   instead of a filled dot — Todoist's checkbox, in the priority colour,
   with a tick drawn (two borders, no glyph and no font) once the work is
   Done or Verified. The ring is scoped to the roadmap row; the hero's
   driver dots and every other `.sevdot` keep their filled shape. ---- */
.irow .t1 .sevdot {
  width: 13px; height: 13px; border-radius: 50%; box-sizing: border-box;
  background: transparent; border: 1.5px solid currentColor; color: var(--low);
  margin-top: 4px; flex-shrink: 0; position: relative;
}
.irow .t1 .sevdot.sev-critical, .irow .t1 .sevdot.sev-high { color: var(--crit); }
.irow .t1 .sevdot.sev-medium { color: var(--warn); }
.irow .t1 .sevdot.sev-low { color: var(--note); }
.irow .t1 .sevdot[data-ring="done"] { color: var(--good); background: var(--good-bg); }
.irow .t1 .sevdot[data-ring="done"]::after {
  content: ""; position: absolute; left: 3px; top: 1px; width: 3.5px; height: 6.5px;
  border: solid currentColor; border-width: 0 1.6px 1.6px 0; transform: rotate(42deg);
}

/* ---- A1 (Linear #1): the accordion body is ONE white sheet — a quiet
   hairline, 10px radius, generous air, and a hairline rule between the
   sections inside it so the eye can find the next one. ---- */
.idetail.inline.acc-body { border-radius: 10px; background: var(--panel); }
.acc-body .idetail-b { padding-top: 14px; }
.acc-body .idetail-f { background: transparent; border-top: 1px solid var(--line); }
.idetail-chips { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.mt6 { margin: 6px 0 0; }
.ev-import-f { margin: 6px 0 14px; }
.ev-more-s { cursor: pointer; }
.ev-why-src { margin: -4px 0 0; }
.chiprow { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 4px; }

/* ---- A1 / A7 (Todoist's "Sub-tasks 0/1" + Jira #8's subtask progress
   bar): the Steps checklist. One line per step, a ring or a tick in
   front of it, and the progress line above — a thin track with the same
   6px height the impact bars use, then "Steps N of M done". ---- */
.tsteps { margin-top: 2px; }
.tsteps-prog { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; max-width: 520px; }
.tsteps-track { flex: 0 0 180px; height: 6px; border-radius: 999px; background: var(--raise); overflow: hidden; }
.tsteps-fill { display: block; height: 100%; border-radius: 999px; background: var(--good); }
.tsteps-n { flex: none; font-size: var(--fs-1); color: var(--mid); font-variant-numeric: tabular-nums; }
.tsteps-list { list-style: none; padding: 0; margin: 0; }
.tstep { display: flex; gap: 9px; align-items: flex-start; padding: 5px 0; }
.tstep + .tstep { border-top: 1px solid var(--soft); }
.tstep-m { flex: none; line-height: 0; margin-top: 2px; color: var(--low); }
.tstep.on .tstep-m { color: var(--good); }
.tstep.on .tstep-t { color: var(--mid); }
.tstep-t { min-width: 0; }

/* ---- A2 (Twenty #3 record fields): the facts strip — an icon, a grey
   label on a fixed column and the value beside it, one per row, no box
   and no prose. The four facts and their words are exactly Phase 7's
   (RM-06). ---- */
.tfacts { margin: 16px 0 0; display: flex; flex-direction: column; gap: 2px; max-width: 520px; }
.tfact { display: flex; align-items: baseline; gap: 8px; font-size: var(--fs-2); padding: 3px 0; }
.tfact .ico { flex: none; color: var(--low); transform: translateY(2px); }
.tfact-l { flex: none; width: 92px; color: var(--low); }
.tfact-v { min-width: 0; color: var(--hi); font-variant-numeric: tabular-nums; }
.tfact-note { color: var(--low); }

/* ---- A1 / A2 (Twenty's note cards): "What the audit read" as a plain
   white card on the sheet — a hairline, 10px radius, nothing louder. ---- */
.tcard { border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px; background: var(--panel); }
.tcard-read { margin: 16px 0 0; }
.tcard-read .ev-table { max-width: none; }
.tcard-read .ev-table-scroll { max-width: none; }

/* ---- A8 (ClickUp #9, the Activity pane — INSIDE the task, never a right
   rail): one row per step of the fix request, the word, then its own date
   right-aligned the way ClickUp times its rows. ---- */
.tact-l { list-style: none; margin: 0; padding: 0; max-width: 520px; }
.tact-i { display: flex; align-items: center; gap: 9px; padding: 6px 0; font-size: var(--fs-2); color: var(--low); }
.tact-i + .tact-i { border-top: 1px solid var(--soft); }
.tact-m { flex: none; line-height: 0; }
.tact-i.on { color: var(--hi); }
.tact-i.on .tact-m { color: var(--good); }
.tact-w { flex: 1 1 auto; min-width: 0; }
.tact-d { flex: none; color: var(--low); font-variant-numeric: tabular-nums; }

/* ---- A3 (Relevance AI #4): the status row as pill TABS — a hairline
   pill, the word, then the count in its own small pale chip. ---- */
.rmap-status { gap: 8px; }
.rmap-st-pill {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: var(--fs-2); padding: 5px 10px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--panel); color: var(--mid);
}
.rmap-st-pill .n {
  font-weight: 500; color: var(--mid); background: var(--raise);
  border-radius: 6px; padding: 0 6px; margin: 0; font-variant-numeric: tabular-nums;
}
.rmap-st-pill:hover { color: var(--hi); }
.rmap-st-pill.on { background: var(--panel2); color: var(--hi); box-shadow: none; border-color: var(--hi); }
.rmap-st-pill.on .n { color: var(--hi); }
.rmap-st-pill.is-off { opacity: .55; background: transparent; }

/* ---- A3 / A5 / A7 (Relevance's search + filter line, Perplexity #6's
   input, Jira #8's light control): search and sort share one line under
   the tabs. The box is the larger one — a hairline, 10px radius and the
   ONE soft shadow — and the sort is the quiet bordered control. ---- */
.rmap-status-row { margin: -2px 0 10px; }
.rmap-tools { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 0 0 14px; }
.rmap-tools .lsearch { margin-left: 0; flex: 1 1 280px; min-width: 0; max-width: 460px; }
.rmap-tools .lsearch-i {
  flex: 1 1 auto; min-width: 0; font-size: var(--fs-3); padding: 10px 14px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
  box-shadow: var(--shadow);
}
.rmap-tools .lsearch-i:focus { border-color: var(--hi); }
.rmap-tools .rmap-sort { flex: none; }
.rmap-tools .rmap-sort select { padding: 8px 10px; border-radius: 8px; }
@media (max-width: 760px) {
  .rmap-tools { flex-direction: column; align-items: stretch; }
  .rmap-tools .lsearch { flex: 1 1 auto; }
  .rmap-tools .rmap-sort { justify-content: space-between; }
}

/* ---- A4 (Sana AI #5): an empty state is a sentence and then a plain
   list of next actions — an icon, the words, a hairline between rows.
   No grey box, no card. ---- */
.ce-acts { list-style: none; margin: 10px 0 0; padding: 0; max-width: 480px; }
.ce-acts li + li { border-top: 1px solid var(--line); }
.ce-act {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 2px; font: inherit; font-size: var(--fs-3); color: var(--hi);
  background: none; border: 0; text-align: left; text-decoration: none; cursor: pointer;
  transition: color var(--motion);
}
.ce-act .ico { flex: none; color: var(--low); }
.ce-act:hover { color: var(--note); }
.ce-act:hover .ico { color: var(--note); }

/* ---- A5 (Perplexity #6): air above the first band of the Roadmap body. The
   header already carries its own rule; the band starts a clear section below
   it. The hero sits inside `.tbody`, so the air goes on the band itself.
   Phase 11, A-2: that band used to be `.home-grid` (the score card). With the
   card gone, "What to do next" is the first band, so it inherits the air —
   the `margin: 12px 0 0` in its own rule was the gap to the card above it,
   which no longer exists. ---- */
.home-next { margin-top: 28px; }
@media (max-width: 760px) { .home-next { margin-top: 18px; } }

/* ---- A6 (Dropbox Dash #7): the site switcher as Dash's rail list — an
   icon tile, the name, one grey line, "Add another site" last. ---- */
.site-menu-b { min-width: 260px; padding: 6px; }
.site-menu-i { grid-template-columns: auto minmax(0, 1fr) auto; gap: 0 10px; padding: 8px 9px; align-items: center; }
.site-menu-tile {
  grid-column: 1; grid-row: 1 / span 2; align-self: center;
  width: 28px; height: 28px; border-radius: 8px; background: var(--raise); color: var(--mid);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-2); font-weight: 600;
}
.site-menu-n { grid-column: 2; }
.site-menu-d { grid-column: 2; }
.site-menu-tick { grid-column: 3; }
.site-menu-add { display: flex; align-items: center; padding-left: 9px; }

/* ---- A9: phones (390). The row already stacks the bars under the title
   (the Phase 6 rule at 760px); the expanded body runs the full width with
   its own air, the facts strip keeps its label column readable, and the
   sticky "Get these fixed" bar is untouched. ---- */
@media (max-width: 760px) {
  .acc-body { margin-left: 0; margin-right: 0; border-radius: 10px; }
  .acc-body .idetail-b { padding-left: 14px; padding-right: 14px; }
  .acc-foot { padding: 0 14px 14px; }
  .tfacts, .tsteps-prog, .tact-l, .tcard-read { max-width: none; }
  .tfact { align-items: flex-start; }
  .tfact-l { width: 84px; }
  .tcard { padding: 12px 13px; }
  .ce-acts { max-width: none; }
}

/* ---- U2-07 (PHASE9_BRIEF.md, UAT2 16 Sep): at ~390 the roadmap carried a
   score card, a "What to do next" band repeating that same card's own
   "Start here" driver, a filter pill row, a status pill row and a search +
   sort row before the list ever started, and the sticky Get these fixed
   button measured under 34px tall against the 44px a thumb needs. Fixed
   together on purpose, same as Ben's finding and the tester's named it as
   one screen:
   - `.home-next` folded away at mobile. **Phase 11, A-2 reverses that one
     rule and the reason is in the ask.** U2-07 could fold this band away only
     because the score card 20px above it carried "Start here: <link>", the
     same task with the same link. Harsha's clip (17 Sep, 0:00-0:26) says the
     opposite of what U2-07 assumed: "no need to show 'Start here' because
     it's a double-up with 'What to do next'" — so the card went, the
     duplicate went with it, and this band is now the ONLY thing on a phone
     that names the next task. Hiding it would leave a phone reader with a
     bare list and no next step. The whole card it was folding into is gone,
     so the band costs less height than the pair ever did.
   - every tappable pill in that chrome grows to a real 44px hit area
     without growing the visible pill: the extra padding is invisible,
     `align-items: center` keeps the label centred in the taller box. ---- */
@media (max-width: 760px) {
  .rmap-filters .chip, .rmap-st-pill {
    min-height: 44px; box-sizing: border-box; display: inline-flex; align-items: center;
  }
  .gtf-cta { min-height: 44px; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; }
  /* Phase 11, A-2: "Open this task" inherits Ben's B-4 rule. B-4 gave the
     score card's phone "Start here" line its own 44px band because the task
     it opened had to be reachable with a thumb. That card is gone and this
     button is now the only thing on a phone roadmap that opens the next task,
     and it measured 29px tall at 390. The rule follows the job, not the
     element it happened to be written for. */
  .home-next-cta { min-height: 44px; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; }
}

/* The chosen mark in the app shell, shipped 2026-09-17. The SVG carries its
   own colours, so no new hex enters the token set. 24px to match the cap
   height of the wordmark beside it. */
.topnav .brand { display: inline-flex; align-items: center; gap: 8px; }
.topnav .brand-mark { border-radius: 5px; display: block; flex: none; }

/* The "to fix first" number in the header block. A class, not an inline
   style: project.html has an inline-style budget that is pinned by a test,
   and a fourth inline colour tipped it over. */
.num .v-crit { color: var(--crit); }

/* ======================================================================
   Phase 10, Lane O (PHASE10_BRIEF.md) — the clip fix list's layout and
   state work. Appended as one block on purpose: every rule here either
   introduces a new class or deliberately OVERRIDES an earlier one, and a
   second builder was editing this file's middle at the same time.
   ====================================================================== */

/* ---- O-3 (H-04): a row click is acknowledged in the same frame, and never
   lands on a blank band. The skeleton is what the click puts on screen while
   the fragment is fetched; `.acc-err` is what a failure leaves in place of a
   whole-page navigation; `.acc-empty` is what an empty fragment says instead
   of white space. ---- */
.acc-skel { padding: 16px; display: flex; flex-direction: column; gap: 10px; }
.acc-skel-l {
  display: block; height: 10px; border-radius: 5px; background: var(--raise);
  animation: acc-pulse 1.1s ease-in-out infinite;
}
.acc-skel-l:nth-child(1) { width: 62%; }
.acc-skel-l:nth-child(2) { width: 92%; }
.acc-skel-l:nth-child(3) { width: 78%; }
@keyframes acc-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) {
  .acc-skel-l { animation: none; }
}
.acc-err { padding: 16px; display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.acc-err .ev-prose { margin: 0; }
.acc-empty { margin: 0; color: var(--mid); }
/* The row scrolls clear of whatever sits above it. Scoped to WIDE widths on
   purpose: S-6 sets its own `scroll-margin-top` for the phone inside its
   `@media (max-width: 640px)` block, and this rule sits later in the file, so
   an unscoped copy here silently overrode the value that lane chose. Flagged
   in the fix-round-1 receipt, resolved here — one owner per width. */
@media (min-width: 641px) {
  .irow-line { scroll-margin-top: 16px; }
}

/* ---- O-1 (H-02): the ask panel's LOADING state. A spinner beside the
   working line, and nothing else — no retry control of any kind until the
   error state actually exists. ---- */
.ask-running { display: flex; align-items: center; gap: 8px; }
.ask-spin {
  width: 12px; height: 12px; flex: none; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--mid);
  animation: ask-spin 900ms linear infinite;
}
@keyframes ask-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .ask-spin { animation-duration: 3s; }
}
.ask-inline .ask-retry, .ask-inline .ask-more { display: inline-block; margin-top: 8px; }

/* ---- O-2 (H-03): the answer names rows of the list below — an ordered list
   of titles, never a second set of task cards. ---- */
.ask-links { margin: 8px 0 0; padding-left: 20px; display: flex; flex-direction: column; gap: 6px; }
.ask-links li { font-size: var(--fs-2); color: var(--mid); }
.ask-links a { color: var(--note); }

/* ---- O-4 (H-01): the project header cannot overlap itself.
   The stat cells wrap a whole cell at a time instead of squeezing into each
   other, and the verdict sentence is its OWN full-width row under the title
   row rather than a narrow column wedged in beside the numbers. No
   `position: absolute` anywhere here.

   Phase 11, A-1 (Harsha's clip, 17 Sep 18:24: "scores are not showing
   properly in Live audit... as we horizontal"): O-4 made this a GRID with
   `repeat(auto-fit, minmax(64px, max-content))` and `min-width: 0` inside a
   flex parent. `min-width: 0` lets the flex item shrink to its MIN-content
   width, and a grid track list of `max-content` tracks then has one column to
   fit in, so all six cells stacked into a single 75px column at 1280 and
   pushed the site name to the bottom of it. Measured on Insights before this
   fix: `.phead .nums` was 75 x 311. The headless check passed because "no two
   cells overlap" is trivially true of a stack.
   It is a WRAPPING FLEX ROW again (the pre-Phase-10 rule at ~382 was flex
   with no wrap). Flex sizes to max-content first and only wraps when the row
   genuinely does not fit, so the six cells share one line at 1280 and 768 and
   wrap onto two lines at 390. `min-width: 0` is deliberately NOT set here:
   it is the thing that let the strip collapse. ---- */
.phead .nums {
  display: flex; flex-wrap: wrap;
  gap: 10px 22px; align-items: flex-end;
}
.phead .num { min-width: 0; }
.phead .num .l { white-space: normal; overflow-wrap: anywhere; }
.phead-verdict {
  flex: 0 0 100%; width: 100%; max-width: none;
  text-align: left; margin-top: 2px; order: 1;
}
@media (max-width: 700px) {
  /* the strip gets the whole row, which is what lets it wrap cleanly */
  .phead .nums { width: 100%; }
}

/* ---- O-5 (M-3, Harsha 2026-09-17): two bands off the phone roadmap.
   (a) "What to do next" is already folded away at mobile (U2-07) and the
       score card's own "Start here" line is the fold — it now opens the row
       in place, which is the one thing the folded band's CTA could do that
       the line could not.
   (b) the status pills move ONTO the search-and-sort line, as a chip strip
       that scrolls sideways INSIDE itself. The last attempt at this widened
       the phone page from 390 to 398 and was reverted: a chip strip that can
       grow past its column. So every box in the chain gets `min-width: 0`
       and the strip itself gets `overflow-x: auto` with no wrapping, which
       is what makes the overflow the STRIP's and never the page's.
   Above 640px `.rmap-ctl` is `display: contents`, so the wide layout the
   Mobbin round pinned is laid out exactly as it was. ---- */
.rmap-ctl { display: contents; }
@media (max-width: 640px) {
  .rmap-ctl {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: 8px; min-width: 0; margin: 0 0 12px;
  }
  .rmap-ctl > .rmap-tools { display: contents; }
  .rmap-ctl .lsearch { order: 1; flex: 1 1 100%; min-width: 0; margin: 0; }
  .rmap-ctl > .rmap-status-row { order: 2; flex: 1 1 0; min-width: 0; max-width: 100%; margin: 0; }
  .rmap-ctl .rmap-sort { order: 3; flex: 0 0 auto; min-width: 0; max-width: 100%; margin: 0; }
  .rmap-ctl .rmap-sort select { max-width: 100%; min-width: 0; }
  .rmap-status {
    flex: 1 1 0; min-width: 0; max-width: 100%;
    flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .rmap-status::-webkit-scrollbar { display: none; }
  .rmap-st-pill { flex: 0 0 auto; white-space: nowrap; }
}

/* ---- Ben fold-in, Phase 10 (BEN_P10_FINDINGS.md) ----------------------- */

/* B-3 (finding 3): at 1280 the left nav's site label broke MID-WORD —
   "hashtagthoughts.co" then "m" on the next line, three line boxes in a
   152px column. `.pnav-brand` carries `overflow-wrap: anywhere` (ledger.css
   ~2241), which is right for a long task title and wrong for a hostname. The
   name is never cut in half now; the page header beside it always carries the
   domain in full, so nothing is lost when a very long one is trimmed. */
.pnav-brand {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  overflow-wrap: normal; word-break: normal;
  /* ...and 2px back off each side, because with the old 10px the longest
     board name on this machine (hashtagthoughts.com, 133px) missed its 132px
     content box by ONE pixel and lost its last letter to the ellipsis. The
     ellipsis is the safety net for a longer name, not the normal case. */
  padding-left: 8px; padding-right: 8px;
}

/* Phase 10's B-1 (the compacted phone score card, its 2x2 driver grid, the
   band legend folded into "How is this calculated?") and B-4 (the phone-only
   full-width "Start here" tap target) were both rules for the score card.
   Phase 11, A-2 removed that card on Harsha's own ask, so every one of those
   rules is gone with it — `.home-bands`, `.home-verdict`, `.home-start`,
   `.home-start-tap`, `.home-start-li`, `.home-score-n`, `.home-score-word`,
   `.home-drivers`, `.home-legend`, `.home-calc`, `.home-grid`. Nothing on the
   Roadmap carries those classes any more, and the height B-1 was clawing back
   at 390 is a whole band larger now than it was then. The score, the priority
   counts and the verdict are in the page header on this tab, exactly as they
   are on every other tab. */

/* ======================================================================
   Phase 11, Lane A (PHASE11_BRIEF.md) — Harsha's own clip, 17 Sep 18:24.
   Appended as one block, the same habit as Phase 10: every rule here either
   introduces a class or deliberately overrides an earlier one.
   ====================================================================== */

/* ---- A-3 (ask 2, 0:26-0:37): "Put some gap between the left side border and
   the 'What should I do first?' button and all the sections below it, because
   it's stuck too much to the left."

   Measured at 1280 before this rule: the page header's site name starts at
   x=197 and so does the "What to do next" band (both sit inside a box with
   28px of side padding — `.phead` and `.tbody`). Everything below them — the
   ask chip, the filter pills, the active filter's explanation line, the status
   strip, the search and sort line, the list head, the impact legend and
   `#tlist` itself — starts at x=169, because they all live in `.rmap-one`,
   which has none. 28px of difference, and the eye reads it as the list
   hanging off the left edge of its own page.

   ONE rule on the ONE wrapper they share, not a padding per block: `.rmap-one`
   takes the same side padding the header and the body boxes already use, so
   the whole tab has a single left edge and it stays single when a block is
   added or removed.

   Above 900px only. At 900 and below the shell drops to a 16px page padding
   and `.rmap-one`'s children already run to the page edge inside it; the ask
   is a desktop one and the phone's own chrome budget was measured down to the
   pixel one phase ago (O-5 / B-1), so nothing here touches it. ---- */
@media (min-width: 901px) {
  .rmap-one { padding-left: 28px; padding-right: 28px; }
}
/* Checker (PHASE11_CHECK.md): below 901px the same blocks sat at x=0 against a
   title at x=16, at 390, 640, 768 and 900 alike. The header uses 16px there
   (`.phead { padding: 16px 16px 12px }`), so the wrapper does too. */
@media (max-width: 900px) {
  .rmap-one { padding-left: 16px; padding-right: 16px; }
}

/* ---- A-4 (ask 3, 0:38-0:50): the clicked row lands just below the sticky
   bar, never under it and never off the top of the window.

   `--acc-top` is written by `roadmap.js` (`accTopGap()`) on load, on resize
   and before every scroll: the height of whatever is stuck to the TOP of the
   window plus a 12px gutter. Above 900px that is the "Get these fixed" bar
   (`position: sticky; top: 0`); at 900 and under the same bar is fixed to the
   BOTTOM of the window instead, so nothing obstructs the top and the value is
   the bare gutter. The fallback in the `var()` is Phase 10's flat 16px, which
   is what a page with the script blocked still gets.

   This is the LAST `scroll-margin-top` on `.irow-line` in the file, so it is
   the one that applies at every width — it deliberately replaces both the
   Phase 10 O-3 rule (16px above 640px) and S-6's 12px phone rule, which each
   owned one side of a breakpoint and neither of which knew about the bar. ---- */
.irow-line { scroll-margin-top: var(--acc-top, 16px); }

/* ======================================================================
   Phase 12, Lane A (PHASE12_BRIEF.md) — his third clip. Appended as one
   block for the same reason Phase 10's was: two other builders are in this
   file at the same time, and every rule here is either a new class or a
   deliberate override of an earlier one.
   ====================================================================== */

/* ---- A-2 (C2, 0:09-0:17: "Put site score in a circle, highlighted, like how
   Lighthouse page-speed test results, so it stands out.")

   The ring is 56px on desktop and 48px on the phone; the SVG is drawn in a
   fixed 56-unit viewBox, so ONE set of geometry scales to both and the number
   inside scales with it. Nothing here names a band colour: the arc's stroke is
   written by the template from `score_tone`, which reads `site_score.BANDS`,
   so the ring and the numeral can never be coloured from two tables.

   The number sits ON the drawing without leaving the flow: the cell is a
   two-row GRID and the ring and the numeral are both placed in row 1, which
   stacks them without `position: absolute`. H-01 (Phase 10, O-4) bans
   out-of-flow positioning anywhere in this header and is swept by a test —
   for good reason, since an absolutely placed cell is exactly how the header
   overlapped itself before. A grid cell cannot overlap anything outside its
   own track. The numeral stays real, selectable text in the same `.v`
   element the header tests read, rather than an SVG `<text>` a screen reader
   would announce as part of a picture. ---- */
.phead .num-score { justify-items: center; text-align: center; }
.score-ring { width: 56px; height: 56px; display: block; }
.score-ring-t { fill: none; stroke: var(--line); stroke-width: 5; }
.score-ring-a { fill: none; stroke-width: 5; stroke-linecap: round; }
.phead .num-score .score-ring, .phead .num-score .v { grid-area: 1 / 1; }
.phead .num-score .v {
  align-self: stretch; justify-self: stretch;
  display: flex; align-items: center; justify-content: center;
}
.phead .num-score .l { grid-area: 2 / 1; }
@media (max-width: 700px) {
  .score-ring { width: 48px; height: 48px; }
  .phead .num-score .v { font-size: var(--fs-4); }
}

/* The strip is `align-items: flex-end` (Phase 10, O-4), which bottom-aligns
   cells of equal height. The ring cell is twice as tall as a text cell, so
   under flex-end its TOP sat 30px above every other cell's: the live header
   check (`_measure_phead.py`, A-1) reads the cells' top edges to tell a row
   from a stack, and two distinct tops is what a stack looks like to it.
   Every cell stretches to the line's height instead, and each lays its own
   content out bottom-up, so the numerals still sit on one baseline, the tops
   are one value again, and the check keeps its teeth. `.nums-cap`'s
   `align-self: flex-end` (line ~2033) is the one exception that has to be
   neutralised, or "open now" alone would drop out of the stretch. */
.phead .nums { align-items: stretch; }
.phead .num, .phead .num.nums-cap {
  align-self: stretch; display: flex; flex-direction: column; justify-content: flex-end;
}
/* the score cell is the one grid in the strip (above); it comes after the
   flex rule on purpose, so `display` is the last word on this cell */
.phead .num.num-score { display: grid; grid-template-rows: auto auto; align-content: end; }

/* ---- A-5 (C9, his message: "I had asked for an add another site function…
   when someone clicks on it they get prompted to upgrade to add more sites…
   that also did not get done.")

   The sidebar item sits at the end of the nav and reads as one of it, with
   the plus sign carrying the "this adds something" meaning rather than a
   second colour. The Settings row is a plain line of text with the control on
   the end of it. ---- */
.pnav-add { color: var(--mid); }
.pnav-add .pnav-plus { width: 20px; text-align: center; font-size: 16px; line-height: 1; flex: none; }
.sites-count { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.addsite-b .ev-prose { margin: 0 0 8px; }
.addsite-f { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }

/* ---- Phase 12 (C4, Lane B): "'What should I do first?' is more like a chat
   box ... make this look and function like someone to chat with you, like a
   chat interface."

   The roadmap's ask was one chip and a collapsible answer panel
   (`.ask-inline`, retired). It is a conversation now: the board's own
   transcript, a box to type in, the presets as chips under it. The message
   language is the reports tab's (`.msg` / `.bub` / `.dots`) re-cut for a panel
   that sits inside the roadmap column rather than owning a pane, so the two
   chats read as the same thing without either one inheriting the other's
   layout.

   ONE thread in the DOM at every width. How much of it opens is a CSS rule,
   not a server decision: the last three exchanges on a desktop, the last one
   on a phone (B-2), and "Show earlier" adds `.open` to lift the cut. ---- */
/* Fix round 2 (Lane A's gutter measurement, 17 Sep): the chat was a padded
   card, so its form started at x=212 at 1280 while the site name and every
   other block on the tab started at 197. Phase 11 A-3's rule is ONE wrapper,
   ONE left edge, and no block may buy its own inset — which is also the
   roadmap's own visual language at this point (`.tlist` drops its card and
   keeps a hairline; the rows sit on the page ground). So the chat keeps no
   side padding and no side border: it is a section of the column separated by
   a hairline, and its edge is `.rmap-one`'s edge at every width. The bubbles
   and the box carry their own borders, which is what makes it read as a chat. */
.ask-chat {
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  background: transparent; padding: 2px 0 14px; margin-bottom: 14px;
}
.ask-earlier {
  display: inline-block; margin: 0 0 8px; font-size: 12.5px;
}
/* Nothing is hidden on a desktop when the whole thread is three or fewer, so
   the control is not offered there. The phone still has one to offer. */
@media (min-width: 901px) { .ask-earlier.only-phone { display: none; } }

.ask-thread {
  max-height: 360px; overflow: hidden auto; overscroll-behavior: contain;
}
.ask-thread:not(.open) > .aexch:nth-last-child(n+4) { display: none; }
/* ...except the exchange the page is actually answering. A link into an older
   answer (`?a=<id>` from the thread, from the scope line, from a saved link)
   must not open on a conversation with that answer folded away. Same
   specificity as the rule above, declared after it, so it wins. */
.ask-thread:not(.open) > .aexch.on { display: block; }
.aexch + .aexch { margin-top: 14px; }

/* ---- one exchange: the question, then the answer ---- */
.amsg { margin-bottom: 8px; }
.amsg:last-child { margin-bottom: 0; }
/* The person's question sits to the right, the app's answer to the left, which
   is the one thing every reader already knows how to read in a chat. The
   bubble hugs its own text (`inline-block`) rather than taking the column: a
   four-word question drawn as a full-width box does not read as a question. */
.amsg.you { text-align: right; }
.ahead { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }
.amsg.you .ahead { justify-content: flex-end; }
.ahead .who, .ahead .when {
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .04em; color: var(--low);
}
.ahead .who { text-transform: uppercase; }
.ahead .who.a { color: var(--note); }
.abub {
  display: inline-block; max-width: 88%; text-align: left;
  background: var(--panel);
  border: 1px solid var(--line); border-radius: 9px; padding: 9px 12px;
  font-size: 13.5px; line-height: 1.6; color: var(--mid);
  overflow-wrap: anywhere;
}
.amsg.you .abub { background: var(--panel2); color: var(--hi); }
.abub.warn { border-color: var(--warn); }
/* The typing indicator: the reports tab's three dots, same animation, same
   reduced-motion behaviour, because it is the same "it is thinking" signal. */
.abub.run { border-color: var(--note); display: inline-flex; gap: 10px; align-items: flex-start; }
.abub .ahead-line { display: block; }
.abub .ask-scope { display: block; margin-top: 6px; font-size: 12px; }
.ask-chat .ask-links { margin: 8px 0 0; }
.aopen, .ask-chat .ask-retry {
  display: inline-block; margin-top: 6px; font-size: 12px;
}

/* ---- the box ---- */
.ask-form { display: flex; gap: 8px; margin-top: 12px; }
.ask-form input[type="text"] {
  flex: 1; min-width: 0; height: 44px; padding: 0 12px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg); color: var(--hi);
  font-family: inherit; font-size: 13.5px;
}
.ask-form input[type="text"]:focus { outline: none; border-color: var(--note); }
.ask-form input[type="text"]::placeholder { color: var(--low); }
.ask-form input[type="text"]:disabled { opacity: .55; cursor: not-allowed; }
.ask-form .btn-primary { flex-shrink: 0; min-height: 44px; padding: 0 18px; }
.ask-suggest { margin-top: 10px; }
.ask-chip-f { display: inline-block; }

/* ---- B-2: the phone. The thread collapses to the input and the last answer,
   every control clears 44px, and nothing is allowed to push the page
   sideways: the bubbles go full width and a suggested question wraps. ---- */
@media (max-width: 900px) {
  .ask-thread { max-height: 300px; }
  .ask-thread:not(.open) > .aexch:nth-last-child(n+2) { display: none; }
  .ask-thread:not(.open) > .aexch.on { display: block; }
  .abub { max-width: 100%; }
  .ask-chat .ask-chips .chip { min-height: 44px; }
  .ask-earlier, .aopen, .ask-chat .ask-retry {
    display: inline-flex; align-items: center; min-height: 44px;
  }
}

/* ---- A-5, the phone (the coordinator, after the first capture): the bottom
   bar is FOUR items and always has been — Roadmap, Live audit, Insights,
   Settings. A fifth made "Add another site" wrap onto two lines inside a 75px
   cell, which is the bar admitting it is full. So the item is a DESKTOP rail
   item, and on a phone the same control is reached from the two places a
   phone user already goes: the site switcher at the top of the screen, where
   it is the FIRST row, and Settings, where it is a full-width row at the top
   of Your site.

   The switcher keeps its DOM order (the item is last in the markup, which is
   the Dropbox Dash rail shape Phase 8 pinned and still reads correctly to a
   screen reader on the desktop menu); `order` moves it to the top of the
   phone's flex column only. ---- */
@media (max-width: 900px) {
  .pnav-add { display: none; }
  .site-menu-add {
    order: -1; min-height: 44px; box-sizing: border-box;
    border-top: 0; border-bottom: 1px solid var(--line);
    margin: 0 0 4px; padding-top: 7px; padding-bottom: 9px;
    border-radius: 6px 6px 0 0;
  }
  /* the Settings control is a row of its own, not a word at the end of a
     sentence, and it carries the 44px target the phone rules ask for */
  .sites-count { display: block; }
  .sites-count > span[aria-hidden] { display: none; }
  .sites-count .lnk {
    display: flex; align-items: center; min-height: 44px; margin-top: 2px;
  }
}

/* ---- Phase 12 fix round 3 (the coordinator's phone measurement, 17 Sep):
   at 390 the chat stood 1424px of page between the header and the first task
   row — "Show earlier", the last exchange, the box, and five suggested
   questions stacked one per line. A roadmap whose first row is off the bottom
   of three screens is not a roadmap.

   Below 640px the chat opens FOLDED, two rows: the box and Send, then the
   fold's control and the suggestions sharing ONE row, the suggestions
   scrolling sideways inside their own strip. The thread is in the same DOM,
   one tap away, and it is ALREADY open whenever the page is answering a real
   ask (`convo-open`, set server-side off `?a=`), so an answer is never behind
   a control. Sharing that second row is what keeps the folded chat inside its
   height budget without shrinking a single tap target below 44px.

   The strip is the Phase 10 O-5 pattern, for the reason it exists there: every
   box in the chain gets `min-width: 0` and the strip itself scrolls, so the
   overflow belongs to the STRIP and can never widen the page. 640px, not 900,
   so the tablet width keeps the layout the earlier rounds measured. ---- */
.ask-foot { display: contents; }
@media (max-width: 640px) {
  .ask-chat {
    display: flex; flex-direction: column; min-width: 0;
    padding: 0 0 8px; margin-bottom: 12px;
  }
  /* the box first, then the shared row; the thread sits above the box when it
     is open, the way the desktop reads */
  .ask-earlier { order: 0; margin: 0 0 6px; }
  .ask-thread { order: 0; max-height: 46vh; }
  .ask-form { order: 1; margin-top: 0; }
  .ask-foot {
    display: flex; order: 2; align-items: center; gap: 10px;
    min-width: 0; max-width: 100%; margin-top: 6px;
  }
  /* FOLDED is the default. `convo-open` is the server's on an answering page,
     and the toggle's otherwise. */
  .ask-chat:not(.convo-open) .ask-thread,
  .ask-chat:not(.convo-open) .ask-earlier { display: none; }
  .ask-convo {
    flex: 0 0 auto; display: inline-flex; align-items: center;
    min-height: 44px; font-size: 12.5px;
  }
  /* ONE strip, scrolling inside itself */
  .ask-suggest {
    flex: 1 1 0; min-width: 0; max-width: 100%; margin: 0;
    flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; gap: 6px;
  }
  .ask-suggest::-webkit-scrollbar { display: none; }
  .ask-suggest .ask-chip-f { flex: 0 0 auto; min-width: 0; }
  .ask-suggest .chip { white-space: nowrap; max-width: none; }
}
/* Above 640px the fold does not exist: no control, and the thread is never
   hidden by the class the phone uses. */
@media (min-width: 641px) { .ask-convo { display: none; } }

/* ======================================================================
   Phase 15, Builder A (PHASE15_BRIEF.md A1-A9, 24 Sep 2026): the brand
   refresh off the mockup (reviews/phase15_mockup.png) and Ben's UAT4
   spacing items (H4-03, H4-08..H4-15). Appended as ONE block, the habit
   Phases 10-12 set: another builder is working in project.html at the same
   time, and every rule here either re-styles an existing selector for the
   new look or styles markup Phase 15 added. Colours read the :root tokens
   only (re-valued above); nothing here names a literal colour.
   ====================================================================== */

/* ---- A3: type. Outfit everywhere the product speaks (18 Oct 2026; the mono face stays
   for URLs and codes only). The scale: page title 28/700, section title
   20/600, task title 16/600, body 14, caption 12, the two hero numbers
   44/700. */
body { -webkit-font-smoothing: antialiased; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ---- A2: the wordmark. Lowercase "seolution" beside the closing-tag mark (18 Oct 2026, brand/BRAND.md §9):
   the word is HTML text in the self-hosted Outfit at 500 / -0.035em, in ink; the mark is static/mark.svg. */
.topnav .brand, .pubnav .brand { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font); font-weight: 500; letter-spacing: -.035em; color: var(--hi); }
.topnav .brand-mark, .pubnav .brand-mark { border-radius: 0; display: block; flex: none; }

/* ---- A4: the shell. Desktop: the sidebar carries the mark + wordmark and
   the icon nav; the project header carries the chip and the avatar, so the
   top bar steps aside on a project page. Phone: the top bar is the only
   header (mark, site switcher, avatar) and the nav is the bottom bar. */
@media (min-width: 901px) {
  body.has-pshell > .topnav { display: none; }
  body.has-pshell .pshell { min-height: 100vh; }
}
.pnav { width: 232px; padding: 24px 16px; border-right: 1px solid var(--line); background: transparent; gap: 4px; }
@media (min-width: 901px) { .pnav { width: 232px; position: sticky; top: 0; height: 100vh; overflow-y: auto; } }
.pnav-brand { display: flex; align-items: center; gap: 10px; padding: 0 8px 28px; font-size: var(--fs-section); font-weight: 500; letter-spacing: -.035em; color: var(--hi); overflow-wrap: normal; }
.pnav-brand:hover { color: var(--hi); }
.pnav-mark { flex: none; display: block; }
.pnav-word { font-weight: 500; letter-spacing: -.035em; }
.pnav-item { gap: 12px; padding: 10px 12px; border-radius: var(--radius-ctl); border: 1px solid transparent; font-size: var(--fs-3); font-weight: 500; color: var(--mid); width: 100%; }
.pnav-item:hover { background: var(--panel); color: var(--hi); }
.pnav-item.on { background: var(--panel2); border-color: var(--panel2-line); color: var(--note); }
/* Phase 15C: the nav icons are data-and-decoration blue (brand Signal blue,
   non-text, 3.68:1 on white, 3.33:1 on the lit tile); the label beside each
   icon keeps its text ink. */
.pnav-ic { width: 20px; height: 20px; flex: none; color: var(--signal); }
.pnav-item .n { background: var(--panel); color: var(--mid); border: 1px solid var(--line); font-family: var(--font); font-size: var(--fs-1); font-weight: 600; }
.pnav-item.on .n { color: var(--note); border-color: var(--panel2-line); }
.pnav-add .pnav-plus { display: none; }
@media (max-width: 900px) {
  .pnav { width: auto; padding: 4px 4px calc(4px + env(safe-area-inset-bottom)); background: var(--panel); border-right: 0; border-top: 1px solid var(--line); gap: 2px; }
  .pnav-brand { display: none; }
  .pnav-item { width: auto; border: 0; padding: 6px 2px; font-size: 10px; gap: 2px; border-radius: 8px; }
  .pnav-item.on { background: transparent; color: var(--note); }
  .pnav-ic { width: 20px; height: 20px; }
}

/* the top bar (phones, and every page outside a project) */
.topnav { background: var(--panel); border-bottom: 1px solid var(--line); min-height: 56px; gap: 10px; flex-wrap: nowrap; }
.topnav .site-menu { min-width: 0; }
.topnav .site-menu-s { max-width: 44vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle; }
@media (max-width: 900px) {
  .topnav { padding: 0 16px; }
  body.has-pshell .topnav .crumb { display: none; }
}

/* the account menu opens from an initials AVATAR (a blue circle, white
   initials): 40px in the project header as in the mockup, 32px in the top
   bar. The name stays in the menu body (G9) and as the summary's label. */
.acct-menu > summary.who { display: inline-flex; align-items: center; gap: 8px; padding: 0; font-family: var(--font); color: var(--mid); }
.acct-menu > summary.who::after { content: none; }
.acct-menu .who-n { display: none; }
.avatar { width: 32px; height: 32px; border-radius: 999px; background: var(--accent); color: var(--accent-ink); font-family: var(--font); font-size: 12px; font-weight: 600; letter-spacing: .02em; display: inline-flex; align-items: center; justify-content: center; }
.acct-menu-head .avatar { width: 40px; height: 40px; font-size: 14px; }
.acct-menu-b { background: var(--panel); border-radius: 12px; min-width: 200px; }
.acct-menu-b a:hover { background: var(--panel2); }
.acct-site.on { color: var(--note); }
@media (max-width: 900px) { .acct-menu-head { display: none; } }

/* ---- A4 / A6: the project header. The domain is the page title with ONE
   subtitle line; top right, the "Last audited" chip and the avatar. At 390
   the title and the chip sit on separate rows BY DESIGN (a column), never
   by an accidental wrap. */
.phead { display: block; padding: 28px 28px 0; border-bottom: 0; }
.phead-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.phead-title { min-width: 0; }
.phead h1 { font-size: var(--fs-7); font-weight: 700; letter-spacing: -.02em; line-height: 1.2; margin: 0; overflow-wrap: anywhere; }
.phead .sub { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 6px; font-family: var(--font); font-size: var(--fs-3); color: var(--mid); }
.phead-right { display: flex; align-items: center; gap: 16px; flex: none; }
.audit-chip { display: inline-flex; align-items: center; gap: 8px; font-size: var(--fs-3); color: var(--mid); white-space: nowrap; }
.audit-dot { width: 10px; height: 10px; border-radius: 999px; background: var(--live); flex: none; }
.phead-acts { align-items: flex-start; margin-top: 8px; }
.phead-acts:empty { display: none; }
@media (max-width: 900px) {
  .phead { padding: 20px 16px 0; }
  .phead-top { flex-direction: column; align-items: flex-start; gap: 10px; }
  .phead h1 { font-size: var(--fs-6); }
  .phead-right { flex: none; }
}

/* ---- A5: the two hero cards, on the Roadmap ONLY since Phase 16 P1 (they were
   on every tab in Phase 15), in the same place and order there.
   A card floats: white, a 1px hairline, 16px radius and the ONE shadow.
   Rows inside a card never float. Side by side on a desktop, stacked at
   phone widths. */
.hero { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 20px; margin: 20px 0 4px; }
@media (max-width: 1000px) { .hero { grid-template-columns: minmax(0, 1fr); gap: 16px; } }
.hcard { position: relative; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-card); box-shadow: var(--shadow); padding: 24px; min-width: 0; overflow: hidden; }
.hcard-t { font-size: var(--fs-section); font-weight: 600; letter-spacing: -.01em; margin: 0; color: var(--hi); }
.hcard-empty { margin: 8px 0 0; font-size: var(--fs-3); color: var(--mid); line-height: 1.5; }

/* card 1: the ring (96px, the primary blue) with the number inside at 44px */
.hcard-score { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 24px; align-items: center; }
/* `.phead .hcard-score ...` (0,4,0) so the Phase 12 strip rules for the
   small 56px ring (`.phead .num.num-score`, a grid, 0,3,0) never reach the
   hero ring. */
.phead .hcard-score .num.num-score { width: 96px; height: 96px; display: grid; grid-template: 96px / 96px; place-items: center; margin: 0; padding: 0; text-align: center; }
.phead .hcard-score .num-score .score-ring, .phead .hcard-score .num-score .v, .phead .hcard-score .num-score .l { grid-area: 1 / 1; }
.phead .hcard-score .num-score .score-ring { width: 96px; height: 96px; display: block; margin: 0; }
.phead .hcard-score .score-ring-t { fill: none; stroke: var(--raise); stroke-width: 6; }
.phead .hcard-score .score-ring-a { fill: none; stroke: var(--signal); stroke-width: 6; stroke-linecap: round; }
.phead .hcard-score .num-score .v { font-family: var(--font); font-size: var(--fs-hero); font-weight: 700; letter-spacing: -.03em; line-height: 1; color: var(--note); }
/* the caption stays in the DOM for a screen reader ("76 site score"), clipped
   to nothing visually; no out-of-flow positioning in the header (H-01) */
.phead .hcard-score .num-score .l { width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.hcard-delta { margin: 6px 0 0; font-size: var(--fs-3); font-weight: 500; }
.hcard-delta.d-down { color: var(--crit); }
.hcard-delta.d-up { color: var(--good); }
.hcard-delta.d-same { color: var(--mid); }
.hcard-score .phead-verdict { margin: 4px 0 0; font-family: var(--font); font-size: var(--fs-2); color: var(--mid); text-align: left; max-width: none; flex-basis: auto; }
.hcard-spark { height: 56px; margin-top: 12px; border-top: 1px dashed var(--line); border-bottom: 1px dashed var(--line); }
.hcard-spark .hcard-empty { margin: 0; line-height: 56px; font-size: var(--fs-2); }
.spark { display: block; width: 100%; height: 56px; overflow: visible; }
/* Phase 15C: the ring and the sparkline are DATA, so they wear brand Signal
   blue (--signal), not the button blue. */
.spark polyline { fill: none; stroke: var(--signal); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.spark .spark-last { stroke: var(--signal); stroke-width: 7; stroke-linecap: round; }
@media (max-width: 620px) {
  .hcard { padding: 20px 16px; }
  .hcard-score { grid-template-columns: 96px minmax(0, 1fr); gap: 16px; }
  .hcard-spark { grid-column: 1 / -1; }
}

/* card 2: AI visibility (Phase 15C, brand_assets/README.md "Recommended AI
   visibility card composition"): the pack's card background as the whole
   card's surface (cover, the card's own radius, the card surface colour
   under it), the pack's network illustration on the left about 32px from the
   edge and vertically centred (decorative, aria-hidden), the words and the
   number on the right. The Phase 15 hand-drawn constellation and its wash
   are gone. */
.hcard-ai { display: grid; grid-template-columns: 160px minmax(0, 1fr); gap: 20px; align-items: center; padding-left: 32px;
  background: url("brand/ai-visibility-card-background.svg") center top / cover no-repeat, var(--ai-card); }
.ai-net { width: 160px; height: auto; display: block; }
/* The artwork's wave fills the bottom band of the card, and text on it
   measured 2.7 to 3.5:1 (the change line on #78B0FF). So the words sit at
   the TOP of the card and their column reserves that band below them; the
   network stays centred on the card. Worst measured contrast of every line
   on the rendered artwork is in PHASE15C_DONE.md. */
.hcard-ai .hcard-body { align-self: start; padding-bottom: 56px; }
/* the brand pack's sparkles: a 16px AI feature mark, on the "Best for AI"
   segment and the "AI visibility" bar label ONLY (README: tabs, pills,
   secondary cards); decorative, the word beside it carries the meaning */
.ai-spark { width: 16px; height: 16px; display: inline-block; vertical-align: -3px; margin-right: 4px; flex: none; }
.ai-n { font-size: var(--fs-hero); font-weight: 700; letter-spacing: -.03em; line-height: 1.05; color: var(--ai); margin-top: 6px; }
.ai-cap { margin: 4px 0 0; font-size: var(--fs-3); color: var(--mid); }
.ai-change { margin: 4px 0 0; font-size: var(--fs-3); font-weight: 500; color: var(--ai); }
.ai-change.d-down { color: var(--crit); }
.ai-change.d-same { color: var(--mid); }
@media (max-width: 620px) {
  .hcard-ai { grid-template-columns: 96px minmax(0, 1fr); gap: 12px; padding-left: 16px; }
  .ai-net { width: 96px; }
}

/* ---- the Roadmap body: "What to do next" and the roadmap itself are
   cards on the cool ground, the same card as the hero */
.tbody { padding-left: 28px; padding-right: 28px; }
.home-next { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-card); box-shadow: var(--shadow); padding: 20px 24px; }
.home-next-t { font-size: var(--fs-4); }
@media (max-width: 900px) { .tbody { padding-left: 16px; padding-right: 16px; } .home-next { padding: 16px; } }

/* ---- A7: the Roadmap card. Title left, the view filters as a segmented
   control right (the selected segment solid blue, white text); the five
   severity chips under them; then search + sort; then the numbered rows. */
.rmap-list { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-card); box-shadow: var(--shadow); padding: 24px; }
.rmap-card-h { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; }
.rmap-card-t { font-size: var(--fs-section); font-weight: 600; letter-spacing: -.01em; margin: 0; }
.rmap-card-h .rmap-filters { margin: 0; }
.rmap-filters, .plan-toggle { display: inline-flex; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-ctl); padding: 3px; gap: 2px; overflow-x: auto; }
.rmap-filters .chip, .plan-toggle .chip { font-family: var(--font); font-size: var(--fs-3); font-weight: 500; padding: 7px 14px; border-radius: 8px; border: 0; color: var(--mid); background: transparent; white-space: nowrap; }
.rmap-filters .chip:hover, .plan-toggle .chip:hover:not(:disabled) { color: var(--hi); background: var(--panel2); }
.rmap-filters .chip.on, .plan-toggle .chip.on { color: var(--accent-ink); background: var(--accent); box-shadow: none; }
.rmap-filters::after { content: none; }

/* H4-14: the five severity chips. A flex row on a desktop; at phone widths
   an explicit TWO-column grid, so the fifth chip is a deliberate cell, never
   a wrapped orphan. */
.sev-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.sev-chip { display: inline-flex; align-items: baseline; gap: 5px; font-size: var(--fs-2); color: var(--mid); background: var(--bg); border: 1px solid var(--line); border-radius: 999px; padding: 4px 12px; white-space: nowrap; }
.sev-chip b { font-weight: 700; color: var(--hi); font-variant-numeric: tabular-nums; }
.sev-chip.sc-high b, .sev-chip.sc-urgent b { color: var(--crit); }
.sev-chip.sc-medium b { color: var(--warn); }
.sev-chip.sc-low b { color: var(--note); }
@media (max-width: 620px) {
  .sev-chips { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .sev-chip { justify-content: flex-start; }
}

/* the rows: a numbered blue circle (our order), the title 16/600, the
   second line = priority pill + page icon + "N pages" (+ searches a month)
   + the status chip; the two labelled impact bars; a chevron. Rows breathe
   (>= 72px) and are separated by hairlines: they sit IN the card. */
.tlist { border-top: 1px solid var(--line); }
.irow { min-height: 72px; padding: 16px 12px; gap: 24px; align-items: center; }
.irow-main { grid-template-columns: minmax(0, 1fr); row-gap: 6px; }
.irow .t1 { display: flex; align-items: center; gap: 14px; grid-column: 1; }
.irow .title { font-size: var(--fs-4); font-weight: 600; line-height: 1.35; color: var(--hi); }
.rank-n { flex: none; width: 28px; height: 28px; border-radius: 999px; background: var(--accent); color: var(--accent-ink); font-size: var(--fs-2); font-weight: 600; display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums; }
.rank-n[data-ring="done"] { background: var(--good-bg); color: var(--good); }
.irow .t2 { grid-column: 1; grid-row: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0; padding-left: 42px; font-family: var(--font); font-size: var(--fs-2); color: var(--mid); }
.irow .t2 .chip { font-family: var(--font); font-size: var(--fs-2); border-radius: 999px; padding: 3px 10px; }
.t2-pages { display: inline-flex; align-items: center; gap: 6px; color: var(--mid); white-space: nowrap; }
.t2-pages .ico { width: 15px; height: 15px; }
.t2-vol { color: var(--mid); white-space: nowrap; }
.irow .t3 { padding-left: 42px; font-size: var(--fs-2); color: var(--low); }
.irow .impbars { flex: 0 0 clamp(220px, 34%, 300px); gap: 10px; }
.irow .impbar { gap: 12px; }
.irow .impbar-l { width: 104px; font-size: var(--fs-2); color: var(--mid); white-space: nowrap; }
.irow .impbar-t { height: 6px; border-radius: 999px; }
.irow .impbar-f { border-radius: 999px; }
.irow .impbar-n { width: 28px; font-size: var(--fs-3); color: var(--hi); text-align: right; font-variant-numeric: tabular-nums; }
.irow-chev { color: var(--mid); }
.irow-chev .ico { transform: rotate(-90deg); transition: transform var(--motion); }
.irow.on .irow-chev .ico, #tlist .irow[aria-expanded="true"] .irow-chev .ico { transform: none; }
.irow.on .irow-chev, #tlist .irow[aria-expanded="true"] .irow-chev { transform: none; }
/* H4-11 (desktop): a right gutter on every row at >= 900px, wider than the
   Ask bubble's footprint, so the bubble floats over white space and never
   over a row's bars, number or chevron. */
@media (min-width: 900px) { .irow { padding-right: 88px; } }
@media (max-width: 900px) {
  .rmap-list { padding: 16px; }
  .irow { padding: 14px 4px; gap: 10px; min-height: 0; }
  .irow .t2, .irow .t3, .irow .impbars { padding-left: 42px; }
  .irow .impbar-l { width: 96px; }
}

/* ---- A8: buttons. Primary = solid blue, white text, 10px radius, 40px
   tall; ghost = white with a hairline. Get these fixed, Open this task,
   Connect, Send request and Set a new password all inherit these. */
.btn-primary { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 40px; padding: 0 18px; border-radius: var(--radius-ctl); font-family: var(--font); font-size: var(--fs-3); font-weight: 600; color: var(--accent-ink); background: var(--accent); border: 1px solid transparent; }
.btn-primary:hover:not(:disabled) { filter: brightness(.94); }
.btn-primary.btn-block, .btn-block.btn-primary { display: flex; width: 100%; }
.btn-ghost { display: inline-flex; align-items: center; justify-content: center; min-height: 40px; padding: 0 16px; border-radius: var(--radius-ctl); font-family: var(--font); font-size: var(--fs-3); font-weight: 500; color: var(--hi); background: var(--panel); border: 1px solid var(--line); }
.btn-ghost:hover:not(:disabled) { border-color: var(--mid); color: var(--hi); }
.btn-sm, .btn-ghost.btn-sm { min-height: 32px; padding: 0 12px; font-size: var(--fs-2); border-radius: 8px; }
/* Fix round (checker M3): a PRIMARY button is a call to action, never a 32px
   in-row control, whatever else it carries: "Open this task", "Choose your
   property", "Yes, have your team contact me", Connect and every other
   `btn-primary btn-sm` keep the 40px primary height on a desktop (A8) and
   the 44px tap target on a phone (the <=760px block at the end of this
   file). `.btn-sm` alone (32px) is for genuinely secondary in-row controls. */
.btn-primary.btn-sm { min-height: 40px; padding: 0 16px; font-size: var(--fs-2); border-radius: var(--radius-ctl); }
.btn-lg { min-height: 44px; }

/* ---- H4-08: the form rhythm. ONE gap token for a form's fields and its
   action, so "Confirm new password" and "Set a new password" never touch
   (the app never loaded public.css's `.fld` rules, which is why they did). */
:root { --form-gap: 16px; }
.panel form .fld, .settings-content form .fld { margin: 0 0 var(--form-gap); }
.panel form .fld > label { display: block; font-size: var(--fs-2); font-weight: 600; color: var(--hi); margin-bottom: 6px; }
.panel form .fld .hint { margin: 6px 0 0; }
.panel form .fld + .btn-primary, .panel form .hint + .btn-primary { margin-top: 0; }

/* ---- H4-09 / H4-13 / H4-03: ONE shared scroll container. Every signed-in
   page renders inside `.app-scroll` (base.html wraps the body block in it),
   and on a phone it alone reserves the bottom: the fixed nav, plus whatever
   is docked above the nav (the Get these fixed bar on the Roadmap, or the
   Ask bubble everywhere else), plus the home-indicator inset. The old
   per-block clearances (152px under the last panel, the roadmap's own
   `.pmain` / `#tlist` padding) are retired here, which is also what removes
   the 160px hole above "Need help?" on the Account page (H4-09): the gap
   there is now the shared section gap. */
:root { --section-gap: 16px; --dock-h: 0px; --nav-bar-h: 0px; }
.app-scroll { padding-bottom: calc(var(--nav-bar-h) + var(--dock-h) + env(safe-area-inset-bottom, 0px)); }
@media (max-width: 900px) {
  :root { --nav-bar-h: 56px; --dock-h: 72px; }
  body:has(.gtf-sticky) { --dock-h: var(--gtf-h); }
  body.no-fab:not(:has(.gtf-sticky)) { --dock-h: 16px; }
  .pmain, body .pmain { padding-bottom: 0; }
  #tlist { padding-bottom: 0; }
  .tbody { padding-bottom: 24px; }
  .tbody > .panel:last-of-type, .waitcard { margin-bottom: 0; }
  #need-help { margin-top: var(--section-gap); }
}

/* ---- H4-03 / H4-11 (phone): the Ask bubble never floats over a task.
   With the Get these fixed bar on screen it DOCKS inside that bar's row, at
   its right edge, and the bar keeps a right gutter so its own button never
   sits under the bubble; everywhere else it sits just above the nav, and
   `.app-scroll` reserves its footprint so the last line can always scroll
   clear of it. */
@media (max-width: 900px) {
  #ask-fab { right: 16px; bottom: calc(var(--pnav-h) + 16px); width: 52px; height: 52px; min-width: 52px; padding: 0; border-radius: 50%; }
  body:has(.gtf-sticky) #ask-fab { bottom: calc(var(--pnav-h) + (var(--gtf-h) - 16px - 52px) / 2); }
  body:has(.gtf-sticky) .gtf-sticky { padding-right: 80px; }
}

/* ---- H4-10: the plan cards are flex columns and the CTA is pinned to the
   bottom with `margin-top: auto`, so both CTAs share one baseline however
   long either card's list is. */
.plan-grid { align-items: stretch; }
.plan-card { display: flex; flex-direction: column; border-radius: var(--radius-card); }
.plan-card > form, .plan-card > a.btn-primary { margin-top: auto; padding-top: 16px; }
.plan-card > a.btn-primary { margin-top: auto; }

/* ---- H4-12: Support (and any form page signed in) keeps the app's 16px
   gutter on a phone; public.css (which styles `.form-wrap` for signed-out
   visitors) is not loaded inside the app, so the gutter is set here. */
.form-wrap { padding: 40px 24px 56px; }
.form-in { max-width: 560px; margin: 0 auto; }
.form-in h1 { font-size: var(--fs-7); font-weight: 700; letter-spacing: -.02em; margin: 0 0 10px; }
.form-in .lede { font-size: var(--fs-3); color: var(--mid); margin: 0 0 20px; line-height: 1.6; }
.support-form .fld { margin: 0 0 var(--form-gap); }
.support-form .fld > label { display: block; font-size: var(--fs-2); font-weight: 600; margin-bottom: 6px; }
@media (max-width: 620px) {
  .form-wrap { padding: 24px 16px 32px; }
  .support-page .form-in { padding-inline: 0; }
}

/* ---- H4-15: the Get these fixed sheet's footer. The explanatory copy and
   "Questions? Send us a message" take their OWN row, above a right-aligned
   action row (Cancel · Send request); 20px padding, deliberate gaps. */
.modal-f.modal-f-stack { display: flex; flex-direction: column; align-items: stretch; gap: 12px; padding: 16px 20px 20px; }
.modal-f-stack .modal-f-copy { margin: 0; flex: none; min-width: 0; line-height: 1.5; }
.modal-f-acts { display: flex; justify-content: flex-end; align-items: center; gap: 12px; }
.modal-b { padding: 20px; }
.modal-h { padding: 16px 20px; }
.modal-card { border-radius: var(--radius-card); }
.modal.sheet .modal-card { border-radius: 0; }
@media (max-width: 620px) { .modal-f-acts { flex-direction: column-reverse; align-items: stretch; } .modal-f-acts button { width: 100%; } }

/* ---- A8: the Ask bubble is blue (the accent, white ink) and the sticky
   request bar is a floating card on the ground. */
.gtf-bar { background: var(--panel); }
@media (min-width: 901px) { .gtf-bar.gtf-sticky { margin: 16px 28px 0; border: 1px solid var(--line); border-radius: var(--radius-card); box-shadow: var(--shadow); padding: 14px 20px; } }

/* ---- the sections under the hero keep the new card language */
.panel { border-radius: 12px; }
.settings-subnav a.on { background: var(--panel2); color: var(--note); }
/* the sign-in card: the mark + wordmark, the card language, the blue button */
.login-box { border-radius: var(--radius-card); box-shadow: var(--shadow); padding: 32px; }
.login-brand { display: flex; align-items: center; gap: 10px; font-weight: 500; letter-spacing: -.035em; }
.login-brand .brand-mark { display: block; }
.login-box form .btn-primary { width: 100%; }
@media (max-width: 620px) { .login-box { margin: 8vh 16px; padding: 24px 20px; } }
/* H4-03: an opened task (roadmap.js and a ?sel= link both scrollIntoView)
   lands CLEAR of the docked layers, never under them. */
@media (max-width: 900px) {
  .acc-body, .irow-line, .home-next, .home-next-cta { scroll-margin-bottom: calc(var(--nav-bar-h) + var(--dock-h) + 16px); }
}

/* the content column sits ON the cool ground (the cards float on it); the
   Phase 6 white sheet round the whole column is retired */
.pmain { background: transparent; border: 0; border-radius: 0; margin: 0; }
/* the Roadmap card's own padding wins over the older one-column pane rules */
.rmap-one .rmap-list.ipane { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-card); box-shadow: var(--shadow); padding: 24px; }
@media (max-width: 900px) { .rmap-one .rmap-list.ipane { padding: 16px; } }
.tbody-top .home-next { margin-top: 0; }
/* H4-14 at phone width: "N open" is the headline chip and takes the full
   first row; the four priorities fill a 2 x 2 grid under it, so no cell is
   ever left alone on a row. */
@media (max-width: 620px) { .sev-chip.sc-open { grid-column: 1 / -1; } }
/* Settings: the sub-nav lines up with the header's 28px gutter (16px on a
   phone) instead of sitting against the sidebar's edge */
@media (min-width: 901px) { .settings-subnav { margin: 24px 0 0 28px; } }
@media (max-width: 900px) { .settings-subnav { padding: 0 16px 4px; margin-top: 12px; } }
/* Insights' section strip on the same 28px gutter as the header */
@media (min-width: 901px) { .tabs.seg-tabs { padding-left: 16px; margin-top: 16px; } }
/* U2-07 still holds under the Phase 15 buttons: on a phone every primary /
   ghost button and every segment is a 44px tap target (the 40px desktop
   height above must not win there). */
@media (max-width: 760px) {
  .btn-primary, .btn-ghost, .rmap-filters .chip, .plan-toggle .chip { min-height: 44px; }
  .rmap-filters .chip, .plan-toggle .chip { display: inline-flex; align-items: center; }
  /* Fix round (checker M3): the compound selectors, so a `btn-sm` class can
     never pull a phone button back under the floor again. Primary = 44px,
     like every other primary; a small secondary control (Collapse, a row's
     Reconnect) = 36px, never 32px under a thumb. Same specificity as the
     desktop compounds above, later in the file, so these win at <=760px. */
  .btn-primary.btn-sm, .home-next-cta.btn-primary { min-height: 44px; }
  .btn-sm, .btn-ghost.btn-sm { min-height: 36px; }
}

/* ---- Phase 15 fix round (PHASE15_FIX_ROUND.md) ------------------------------
   L1: the Roadmap filters at 390. The segmented control sat in the card's
   flex title row with `min-width: auto`, so it could not shrink below its
   454px of content and ran out of the 322px card with "Quick wins" and "All"
   cut off. It now takes the full row on a phone and scrolls INSIDE itself;
   the fade (U3-27's rule above) says which side has more, and the small
   script beside the markup scrolls the selected segment into view and sets
   `data-edge` (start | mid | end | none) as it scrolls, so the fade never
   hides the last segment once you have scrolled to it. */
@media (max-width: 760px) {
  .rmap-card-h .rmap-filters { flex: 1 1 100%; min-width: 0; max-width: 100%; }
  .rmap-filters[data-edge="none"] { -webkit-mask-image: none; mask-image: none; }
  .rmap-filters[data-edge="end"] {
    -webkit-mask-image: linear-gradient(to right, transparent, var(--hi) 26px);
    mask-image: linear-gradient(to right, transparent, var(--hi) 26px); }
  .rmap-filters[data-edge="mid"] {
    -webkit-mask-image: linear-gradient(to right, transparent, var(--hi) 26px, var(--hi) calc(100% - 26px), transparent);
    mask-image: linear-gradient(to right, transparent, var(--hi) 26px, var(--hi) calc(100% - 26px), transparent); }
}

/* L7: the Get these fixed sheet's copy row takes the full width and wraps
   as normal text, and the support link never breaks inside itself: it moves
   to the next line whole ("Questions? Send us a message"). */
.modal-f-stack .modal-f-copy { width: 100%; white-space: normal; }
.modal-f-copy a { white-space: nowrap; }

/* L5: "Your current plan" is a plain label chip in the CTA slot, not a
   disabled button (a washed-out button read as broken). */
.plan-card > .plan-current { margin-top: auto; padding-top: 16px; }
.plan-current-chip { display: inline-flex; align-items: center; min-height: 32px; padding: 0 12px;
  border-radius: 999px; background: var(--panel2); color: var(--note); font-size: var(--fs-2); font-weight: 600;
  border: 1px solid var(--panel2-line); }

/* L6: the AI card says when the number was checked, under the number. */
.hcard-ai .ai-when { margin: 2px 0 0; font-size: var(--fs-1); color: var(--mid); }

/* ---- Phase 15 fold-in (PHASE15_FOLD_IN.md) ----------------------------------
   The lockup, sized ONCE, from the :root pair --lockup-mark / --lockup-word
   (the reasoning is beside the tokens). Full = sidebar and sign-in; compact =
   the top bar (every phone) and the public nav, the same pair x 6/7. The
   sign-in word used to compute 18px: `.login-box h1` (0,1,1) beat
   `.login-brand` (0,1,0). The two-class selector below wins, so it is 24px.
   18 Oct 2026: the mark is sized in em off these word sizes (.wm, the lockup block below). */
.pnav-word, .login-box .login-brand { font-size: var(--lockup-word); }
/* the tracking is set ON the word: an em set on the parent link resolves
   against the link's own 14px and is inherited as a fixed -0.56px */
.topnav .brand-word, .pubnav .brand-word { font-size: calc(var(--lockup-word) * 6 / 7); letter-spacing: -.035em; }

/* Ben P15 (MED): at <=760px the roadmap row stacks as a column whose items
   are centred, and the bars block kept its desktop `flex: 0 0 clamp(...)`,
   which in a column is a 220px HEIGHT, while its width shrank to the words,
   so every track computed to 0px wide. On a phone the block takes the full
   row width under the row's text, one bar per line, each a compact track
   between its label and its number: the same meaning as the desktop bars.
   The chevron stays below it and nothing covers it. */
@media (max-width: 760px) {
  .irow .impbars { flex: none; align-self: stretch; width: auto; flex-direction: column; gap: 8px; }
  .irow .impbar { width: 100%; }
  .irow .impbar-t { flex: 1 1 auto; min-width: 48px; height: 6px; }
}

/* P15 fold-in (lead, on fix_conn_390.png): "Skip for now" under the
   nothing-matched card is a real secondary control with room above it. */
.conn-sub .conn-skip { margin-top: 12px; }

/* ---- Phase 16 (PHASE16_BRIEF.md) ----------------------------------------------
   P2: the Roadmap's AI visibility number is a link to the Insights AI section
   (/insights?section=rankings#ai). The number is the link text; its ::after
   stretches over the whole card (`.hcard` is position: relative), so the
   card is the tap target on a phone and a click target on a desktop. */
.hcard-ai .ai-link { color: inherit; text-decoration: none; }
.hcard-ai .ai-link::after { content: ""; position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
.hcard-ai .ai-link:hover, .hcard-ai .ai-link:focus-visible { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 6px; }
.hcard-ai:has(.ai-link) { cursor: pointer; }
.hcard-ai:has(.ai-link:hover) { border-color: var(--panel2-line); }

/* P4: the Insights AI section. One system with the rest of the app: marks
   in the validated pair --ch-1 (answered, other sites) and --ch-3 (cites
   this site, you), checked in the neutral track, labels in text inks only,
   every bar labelled with its word and number (never colour alone). */
.aiv { margin-top: 14px; padding: 24px; border-radius: var(--radius-card); box-shadow: var(--shadow); }
.aiv-t { font-size: var(--fs-section); font-weight: 600; letter-spacing: -.01em; margin: 0; color: var(--hi); }
.aiv-sub { margin: 4px 0 0; font-size: var(--fs-3); color: var(--mid); line-height: 1.5; }
.aiv-head { margin: 8px 0 0; font-size: var(--fs-3); font-weight: 600; color: var(--hi); }
.aiv-strip { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr); gap: 12px; margin-top: 16px; }
.aiv-tile { border: 1px solid var(--line); border-radius: var(--radius-ctl); padding: 16px; min-width: 0; background: var(--panel); }
.aiv-tile-l { margin: 0; font-size: var(--fs-2); color: var(--mid); line-height: 1.4; }
.aiv-kpi { font-size: var(--fs-7); font-weight: 700; letter-spacing: -.02em; color: var(--hi); line-height: 1.2; margin-top: 4px; }
.aiv-delta { margin: 4px 0 0; font-size: var(--fs-2); font-weight: 500; }
.aiv-delta.d-up { color: var(--good); }
.aiv-delta.d-down { color: var(--crit); }
.aiv-delta.d-same { color: var(--mid); }
.aiv-score { display: grid; grid-template-columns: 100px minmax(0, 1fr); column-gap: 14px; align-items: center; }
.aiv-score .aiv-tile-l { grid-column: 2; }
.aiv-gauge { grid-row: 1 / span 2; width: 100px; height: 56px; display: block; }
.aiv-gauge-t, .aiv-gauge-a { fill: none; stroke-width: 8; stroke-linecap: round; }
.aiv-gauge-t { stroke: var(--raise); }
.aiv-gauge-a { stroke: var(--ch-3); }
.aiv-score-v { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.aiv-pct { font-size: var(--fs-7); font-weight: 700; letter-spacing: -.02em; color: var(--hi); }
.aiv-band { font-size: var(--fs-2); font-weight: 600; padding: 2px 10px; border-radius: 999px; background: var(--raise); color: var(--mid); }
.aiv-band.b-low { background: var(--warn-bg); color: var(--warn); }
.aiv-band.b-medium { background: var(--note-bg); color: var(--note); }
.aiv-band.b-high { background: var(--good-bg); color: var(--good); }
/* fold-in M2: the band cut-offs, one small line under the chip */
.aiv-bands { flex-basis: 100%; font-size: var(--fs-1); color: var(--low); line-height: 1.4; }
.aiv-grid2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; margin-top: 16px; }
.aiv-card { border: 1px solid var(--line); border-radius: var(--radius-ctl); padding: 16px; min-width: 0; margin-top: 12px; background: var(--panel); }
.aiv-grid2 .aiv-card { margin-top: 0; }
.aiv-h { margin: 0; font-size: var(--fs-4); font-weight: 600; color: var(--hi); }
.aiv-card .note-t { margin: 6px 0 0; }
.aiv-key { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 10px; font-size: var(--fs-1); color: var(--mid); }
.aiv-key .k { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 6px; vertical-align: -1px; }
.k-checked, .aiv-bar-f.f-checked { background: var(--low); }
.k-answered, .aiv-bar-f.f-answered { background: var(--ch-1); }
.k-cited, .aiv-bar-f.f-cited { background: var(--ch-3); }
.aiv-plat { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 14px; }
.aiv-plat-n { display: block; font-size: var(--fs-3); font-weight: 600; color: var(--hi); margin-bottom: 4px; }
.aiv-bar { display: grid; grid-template-columns: 96px minmax(0, 1fr) 28px; gap: 10px; align-items: center; font-size: var(--fs-2); color: var(--mid); min-height: 20px; }
.aiv-bar-t { display: block; height: 8px; border-radius: 4px; background: var(--raise); overflow: hidden; min-width: 0; }
.aiv-bar-f { display: block; height: 100%; border-radius: 0 4px 4px 0; }
.aiv-bar-v { text-align: right; font-variant-numeric: tabular-nums; color: var(--hi); font-weight: 600; }
.aiv-riv { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 6px; }
.aiv-riv li { display: grid; grid-template-columns: 22px minmax(0, 1.4fr) minmax(40px, 1fr) auto; gap: 10px; align-items: center; font-size: var(--fs-2); color: var(--mid); padding: 4px 6px; border-radius: 8px; }
.aiv-riv li.is-you { background: var(--panel2); }
.aiv-riv li.aiv-riv-gap { margin-top: 6px; }
.aiv-riv-r { color: var(--low); font-variant-numeric: tabular-nums; text-align: right; }
.aiv-riv-d { color: var(--hi); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aiv-riv-n { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* fold-in LOW: our row below the ten says where it stands, under its domain */
.aiv-riv-note { grid-column: 2 / -1; font-size: var(--fs-1); color: var(--low); }
.aiv-riv-note:empty { display: none; }
.aiv-you { display: inline-block; font-size: var(--fs-1); font-weight: 600; padding: 1px 8px; border-radius: 999px; background: var(--note); color: var(--accent-ink); margin-left: 4px; }
.aiv-mx-wrap { position: relative; overflow-x: auto; margin-top: 10px; -webkit-overflow-scrolling: touch; }
.aiv-mx { border-collapse: separate; border-spacing: 4px; width: 100%; min-width: 520px; font-size: var(--fs-2); }
.aiv-mx th { text-align: left; font-weight: 600; color: var(--mid); padding: 6px 8px; white-space: nowrap; }
.aiv-mx tbody th { color: var(--hi); font-weight: 500; position: sticky; left: 0; background: var(--panel); z-index: 1; }
.aiv-mx td { padding: 0; border-radius: 8px; }
.aiv-mx td a, .aiv-mx td span { display: flex; flex-direction: column; justify-content: center; min-height: 44px; padding: 6px 10px; border-radius: 8px; color: inherit; text-decoration: none; }
.aiv-mx td a:hover, .aiv-mx td a:focus-visible { outline: 2px solid var(--hi); outline-offset: -2px; }
.aiv-mx td small { font-size: var(--fs-1); color: var(--good); font-weight: 500; }
.aiv-mx .mx-cited { background: var(--good-bg); color: var(--good); font-weight: 600; }
.aiv-mx .mx-answered { background: var(--raise); color: var(--hi); }
.aiv-mx .mx-empty { color: var(--low); border: 1px dashed var(--line); }
.aiv-mx .mx-none { color: var(--low); }
.aiv-trend { display: block; width: 100%; max-width: 400px; height: auto; margin-top: 10px; overflow: visible; }
.aiv-trend-g { stroke: var(--line); stroke-width: 1; }
.aiv-trend-y, .aiv-trend-x { font-size: 11px; fill: var(--low); font-variant-numeric: tabular-nums; }
.aiv-trend-v { font-size: 12px; font-weight: 600; fill: var(--hi); }
.aiv-trend-l { fill: none; stroke: var(--ch-3); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.aiv-trend-d { fill: var(--ch-3); stroke: var(--panel); stroke-width: 2; }
.aiv-trend-hit { fill: transparent; }
.aiv-trend-p:hover .aiv-trend-d { r: 6; }
.aiv-answers { display: grid; gap: 8px; margin-top: 10px; }
.aiv-ans { border: 1px solid var(--line); border-radius: var(--radius-ctl); background: var(--panel); scroll-margin-top: 16px; }
.aiv-ans.is-cited { border-color: var(--good); }
.aiv-ans:target { outline: 2px solid var(--hi); outline-offset: 2px; }
.aiv-ans summary { cursor: pointer; padding: 12px 14px; font-size: var(--fs-3); color: var(--hi); line-height: 1.45; }
.aiv-ans.is-cited summary { font-weight: 600; }
.aiv-src { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 16px; padding: 0 14px 14px; }
.aiv-src-h { margin: 0 0 6px; font-size: var(--fs-2); font-weight: 600; color: var(--mid); }
.aiv-src ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.aiv-src li { display: grid; grid-template-columns: 24px minmax(0, 1fr); column-gap: 8px; font-size: var(--fs-2); line-height: 1.4; }
.aiv-src li a { color: var(--hi); overflow-wrap: anywhere; }
.aiv-src-n { grid-row: 1 / span 2; color: var(--low); font-variant-numeric: tabular-nums; text-align: right; }
.aiv-src-d { grid-column: 2; color: var(--low); font-size: var(--fs-1); overflow-wrap: anywhere; }
@media (max-width: 900px) {
  .aiv-strip { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .aiv-strip .aiv-score { grid-column: 1 / -1; }
  .aiv-grid2 { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 620px) {
  .aiv { padding: 16px; }
  .aiv-src { grid-template-columns: minmax(0, 1fr); }
  .aiv-bar { grid-template-columns: 88px minmax(0, 1fr) 24px; }
  /* when the grid is wider than the card (more than three platforms) it
     scrolls sideways under a fade on its right edge (the fade is switched on
     by the section's script only when it really overflows); the search
     column stays put (sticky) so every row keeps its name */
  .aiv-mx-wrap.can-scroll { mask-image: linear-gradient(to right, black calc(100% - 28px), transparent); -webkit-mask-image: linear-gradient(to right, black calc(100% - 28px), transparent); }
  /* three platforms fit a 390 screen; a fourth or fifth pushes the table
     past the card and it scrolls under the fade */
  .aiv-mx { min-width: 0; border-spacing: 2px; }
  .aiv-mx td { min-width: 72px; }
  .aiv-mx td a, .aiv-mx td span { padding: 6px 5px; }
  /* fold-in LOW: a search breaks only between words ("psycholog y" was a mid-word
     break); the column is wide enough for one long word */
  .aiv-mx tbody th { white-space: normal; width: 96px; min-width: 96px; max-width: 96px; padding: 6px 4px; overflow-wrap: normal; word-break: normal; hyphens: manual; }
  .aiv-mx thead th { white-space: normal; padding: 6px 4px; font-size: var(--fs-1); }
  .aiv .aiv-swipe { display: inline; }
  /* the domain on its own line above its bar, so a long name is not cut to a few letters */
  .aiv-riv li { grid-template-columns: 22px minmax(0, 1fr) auto; }
  .aiv-riv li .aiv-bar-t { grid-column: 2 / -1; grid-row: 2; }
  .aiv-riv-d { white-space: normal; overflow-wrap: anywhere; }
}
.aiv-swipe { display: none; }
/* P2: the one line at the top of Rankings that jumps to the AI section */
.aiv-jump { margin: 8px 0 0; font-size: var(--fs-3); }
.aiv-jump a { color: var(--note); font-weight: 500; }
.hcard-ai .ai-more { margin: 6px 0 0; font-size: var(--fs-2); font-weight: 600; color: var(--note); }

/* 18 Oct 2026: the travelling light of the previous lockup (a glow overlay and a tab-icon script) is retired with it; see the lockup block below. */
/* The lockup (18 Oct 2026, revised the same day; brand/BRAND.md §9). Harsha: "<seolution/> looks more like a
   meta tag. make the logo like that and dont take away the glow animation you did before."
   The word sits INSIDE a self-closing tag: a blue "<" (static/tag-open.svg), the word in Outfit 500 at
   -0.035em in ink, a blue "/>" (static/tag-close.svg). Both brackets are sized in em off the word, so every
   lockup keeps one ratio: each bracket box is 1em tall and drops .26em below the baseline, which centres the
   chevrons on the word's x-height. Widths follow the drawings (34 and 60 units of 64).
   The light (one 3.6 s lap, brand/logo/make_logo.py LAP): it runs along "<" (0-20%, glow-open.svg), sweeps
   the word (20-62%, the gradient below), then runs along "/" and ">" (62-100%, glow-close.svg). Reduced
   motion: no movement, a still halo on each bracket. */
.wm { display: inline-block; white-space: nowrap; }
.wm .wm-lt, .wm .wm-gt { position: relative; display: inline-block; height: 1em; vertical-align: -.26em; }
.wm .wm-lt { width: .531em; margin-right: .02em; }
.wm .wm-gt { width: .9375em; margin-left: .03em; }
.wm .wm-lt > img, .wm .wm-gt > img { display: block; width: 100%; height: 100%; }
.wm .wm-lt::after, .wm .wm-gt::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: no-repeat center / 100% 100%; }
.wm .wm-lt::after { background-image: url(glow-open.svg?v=5895d700); }
.wm .wm-gt::after { background-image: url(glow-close.svg?v=5453b969); }
.wm .wm-word { color: var(--hi); }
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .wm .wm-word {
    background: linear-gradient(100deg, var(--hi) 0%, var(--hi) 42%, var(--wm-light-a) 46%,
      var(--wm-light-c) 50%, var(--wm-light-b) 54%, var(--hi) 58%, var(--hi) 100%);
    background-size: 300% 100%; background-position: 100% 0;
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
    animation: wm-sweep 3.6s ease-in-out infinite;
  }
}
@keyframes wm-sweep { 0%, 20% { background-position: 100% 0; } 62%, 100% { background-position: 0% 0; } }
@media (prefers-reduced-motion: reduce) {
  .wm .wm-lt::after { background-image: url(glow-open-still.svg?v=ff76b834); }
  .wm .wm-gt::after { background-image: url(glow-close-still.svg?v=9f16dad7); }
  .wm .wm-word { animation: none; background-position: 100% 0; }
}
/* ---- end Phase 17 G1 */

/* ---- Phase 17 A (prices + Square checkout): plan table, trial-end, paywall, billing */
.plan-price .plan-per { white-space: nowrap; }
.plan-billed { margin: -6px 0 0; font-size: var(--fs-2); color: var(--mid); }
.plan-billed .plan-save, .plan-offer .plan-save { color: var(--good); font-weight: 600; white-space: nowrap; }
.plan-extra, .plan-fix, .plan-change-note { margin-top: 10px; }
.plan-card > .plan-act, .plan-card > .plan-buy { margin-top: auto; padding-top: 16px; }
.plan-card > .plan-act .plan-buy { margin: 0; }
.plan-quick { margin-top: 10px; }
.plan-quick-list { list-style: none; margin: 8px 0 0; padding: 0; }
.plan-quick-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding: 10px 0; border-top: 1px solid var(--line); }
.plan-quick-row:first-child { border-top: 0; }
.plan-quick-name { flex: 1 1 140px; min-width: 0; }
.plan-quick-price { font-family: var(--font); font-weight: 600; white-space: nowrap; }
.plan-quick-price .plan-per { font-weight: 400; font-size: var(--fs-1); color: var(--mid); }
.plan-quick .plan-buy { margin: 0; }
.plan-quick-foot { margin: 6px 0 0; }
.plan-quick-foot .lnk { margin-left: 6px; }
.plan-offer { margin-top: 12px; }
.plan-offer .plan-price { margin-bottom: 4px; }
.plan-offer .plan-buy { margin-top: 12px; }
.bill-banner { margin: 0 0 16px; }
.pmain > .bill-banner { margin: 16px 28px 0; }
.bill-failed { border-color: var(--crit); }
.bill-failed .pbanner-t { color: var(--hi); }
.keep-row { display: flex; align-items: center; gap: 8px; min-height: 44px; font-size: var(--fs-3); }
.bill-history td, .bill-history th { vertical-align: top; }
@media (max-width: 760px) {
  .plan-quick-row { align-items: flex-start; }
  .plan-quick .plan-buy, .plan-quick .plan-buy .btn-primary { width: 100%; }
  .plan-toggle .chip { padding-left: 12px; padding-right: 12px; }
  .pmain > .bill-banner { margin: 12px 16px 0; }
  /* checker LOW: a readable two-column table at 390, no per-cell wrap:
     date | amount on one line, plan | status + receipt under it */
  .bill-history tr:first-child { display: none; }
  .bill-history tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px 12px; padding: 10px 0; border-bottom: 1px solid var(--line); }
  .bill-history td { padding: 0; border: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .bill-history td:nth-child(1) { order: 1; }
  .bill-history td:nth-child(3) { order: 2; text-align: right; }
  .bill-history td:nth-child(2) { order: 3; color: var(--mid); }
  .bill-history td:nth-child(4) { order: 4; text-align: right; }
  .bill-history td:nth-child(5) { order: 5; grid-column: 1 / -1; }
  .bill-history td:nth-child(5):empty { display: none; }
}
/* ---- end Phase 17 A */

/* ---- Phase 17 A: vouchers */
.plan-voucher { margin-top: 12px; }
.plan-voucher .btn-link { font-size: var(--fs-2); }
.plan-voucher-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; }
.plan-voucher-input { flex: 1 1 180px; min-width: 0; max-width: 260px; min-height: 40px; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius-ctl); background: var(--panel); color: var(--hi); font-family: var(--font-mono); text-transform: uppercase; }
.plan-voucher-csrf { display: none; }
.plan-voucher-msg { margin: 6px 0 0; color: var(--good); }
.plan-voucher-msg.bad { color: var(--crit); }
.vpr.vp-on, .vpt.vp-on { color: var(--good); }
.voucher-on .plan-save { display: none; }
.voucher-new .pbanner-t { font-size: var(--fs-5); letter-spacing: .08em; }
/* ---- end Phase 17 A vouchers */

/* ---- Ben P16/17 fold-in */
/* L1: "Save up to 23%" is a small badge beside the Annual segment, outside
   the pill, at every width (the pill keeps Monthly | Annual only). */
.plan-toggle-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 4px 0 12px; }
.plan-toggle-row > .plan-toggle { margin: 0; }
.plan-save-badge { display: inline-flex; align-items: center; font-size: var(--fs-1); font-weight: 600; line-height: 1.2; color: var(--good); background: var(--good-bg); border-radius: 999px; padding: 3px 8px; white-space: nowrap; }
/* Trial strip (Harsha 8 Oct 2026, replaces the H1 fold-out): one thin line
   at every width, no plans or prices, so the board comes first. */
.pmain > .bill-strip { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0; padding: 6px 28px; border: 0; border-bottom: 1px solid var(--line); border-radius: 0; background: var(--panel2); font-size: var(--fs-2); line-height: 1.4; color: var(--mid); }
.bill-strip-a { color: var(--note); white-space: nowrap; text-decoration: none; font-weight: 600; }
.bill-strip-a:hover { text-decoration: underline; }
@media (max-width: 760px) {
  .pmain > .bill-strip { padding: 6px 16px; margin: 0; }
}
/* ---- end Ben P16/17 fold-in */

/* ---- Phase 18, Lane S (UAT6) ---------------------------------------------- */
/* U6-04: a refusal banner without an inline style (the style-count pins). */
.pbanner.crit { border-color: var(--crit); }
/* U6-04: the "confirm your email before you pay" row above the plan table. */
.verify-pay { border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; background: var(--panel2); }
/* H4-03 (phone): the only fixed layer is the bottom nav. The Get these fixed
   strip is in the page flow (project.html), the round Ask bubble is gone and
   Ask is a nav item, so the page reserves the nav's height and nothing else. */
.pnav-ask { display: none; }
button.pnav-item { background: none; border: 0; font-family: inherit; line-height: inherit; cursor: pointer; }
@media (max-width: 900px) {
  .pnav-ask { display: flex; }
  .pnav-ask[aria-expanded="true"] { color: var(--hi); }
  #ask-fab, #ask-fab.ask-fab-staff { display: none; }
  :root { --dock-h: 16px; }
  body:has(.gtf-sticky), body.no-fab:not(:has(.gtf-sticky)) { --dock-h: 16px; }
  body:has(.gtf-sticky) .gtf-sticky { padding-right: 16px; box-shadow: none; }
  .gtf-bar.gtf-sticky { margin: 0 16px 16px; border: 1px solid var(--line); border-radius: var(--radius-card); background: var(--panel); }
  .gtf-sticky .gtf-cta { width: 100%; text-align: center; }
  /* the Ask panel opens ABOVE the nav, so the nav (and its Ask item) stay visible */
  #ask-panel, #ask-fab.ask-fab-staff ~ #ask-panel {
    left: 0; right: 0; bottom: var(--pnav-h); width: auto; max-width: none;
    max-height: calc(85vh - var(--pnav-h)); border-radius: 12px 12px 0 0; }
}
/* U6-15 (phone): every suggestion is reachable. Inside the Ask panel the
   suggestions WRAP onto as many lines as they need, never a strip that runs
   off the right edge. */
@media (max-width: 640px) {
  #ask-panel .ask-foot { flex-wrap: wrap; }
  #ask-panel .ask-suggest { flex: 1 1 100%; flex-wrap: wrap; overflow: visible; }
  #ask-panel .ask-suggest .ask-chip-f { flex: 0 1 auto; max-width: 100%; }
  #ask-panel .ask-suggest .chip { white-space: normal; text-align: left; max-width: 100%; }
}
/* U6-15: the 20-second give-up, a real button. */
.ask-retry.btn-ghost { display: inline-flex; align-items: center; margin-top: 8px; }
/* U6-16 (phone): the five roadmap views wrap onto two rows, so every one is
   on screen at 390 and none hides behind a fade. */
@media (max-width: 640px) {
  .rmap-filters { flex-wrap: wrap; overflow: visible; max-width: 100%; }
  .rmap-filters[data-edge] { -webkit-mask-image: none; mask-image: none; }
}
/* U6-09: the legal entity line under the public footer (hidden while unset). */
.pubfoot-legal { max-width: 1120px; margin: 8px auto 0; padding: 0 16px; font-size: 12px; color: var(--low); }
.plan-payments { margin-top: 4px; }
/* H4-07 (phone): the billing history is a stacked two-column list at 390 (Ben
   P16/17), so nothing in it scrolls sideways; the phone-wide `.scrollx` fade
   (its "more to the right" hint) was fading the amount and the status. */
@media (max-width: 760px) {
  #invoices .scrollx { -webkit-mask-image: none; mask-image: none; }
}
/* ---- end Phase 18 Lane S */

/* ---- Phase 18 fix round (PHASE18_CHECK.md) --------------------------------- */
/* H3 (the phone, 390x844): with a conversation on screen the suggested
   questions took ~300px of the Ask panel and left the thread 121px, so the
   why-answer (about 600px) could not be read. Once there is a conversation
   (`has-convo`, set by the server) the suggestions fold behind ONE control
   (`data-ask-sugg`, report.js), the greeting goes, and the panel keeps its
   full height so the thread takes whatever is left and scrolls on its own. */
.ask-sugg-t { display: none; }
@media (max-width: 900px) {
  #ask-panel:has(.ask-chat.has-convo) { height: calc(85vh - var(--pnav-h)); }
  #ask-panel:has(.ask-chat.has-convo) .ask-greet { display: none; }
  .ask-chat.has-convo:not(.sugg-open) .ask-suggest { display: none; }
  .ask-chat.has-convo .ask-sugg-t {
    display: inline-flex; align-items: center; min-height: 44px; font-size: 12.5px;
  }
  .ask-chat.has-convo.sugg-open .ask-suggest { max-height: 40vh; overflow-y: auto; }
}
/* folded (a phone opening Ask with no answer on screen): only as tall as the box */
@media (max-width: 640px) {
  #ask-panel:has(.ask-chat.has-convo:not(.convo-open)) { height: auto; }
}
/* the same on Insights › Ask: the conversation takes the height */
@media (max-width: 640px) {
  .composer.has-convo:not(.sugg-open) .ask-suggest-r { display: none; }
  .composer.has-convo .ask-sugg-t {
    display: inline-flex; align-items: center; min-height: 44px; font-size: 12.5px; margin: 0 0 6px;
  }
  .cpane:has(.composer.has-convo:not(.sugg-open)) .cstream { max-height: calc(100vh - 240px); }
}
/* Low (trend wording): "Trend starts after your second audit" is longer
   than the old words and wrapped inside the 56px one-line strip, so its end
   was cut off at 390. The empty strip grows to fit its sentence. */
.hcard-spark:has(> .hcard-empty) { height: auto; min-height: 56px; display: flex; align-items: center; }
.hcard-spark .hcard-empty { line-height: 1.4; padding: 8px 0; }
/* H2: the update's chip now says what its table says in words ("Impressions
   up from a very low base; clicks unchanged at 0; still below last year"),
   longer than "Improved · +363%", so it wraps instead of running off a phone. */
.gcard-chips .chip { white-space: normal; max-width: 100%; }
/* ---- end Phase 18 fix round */

/* ---- Phase 18 fold-in (BEN_P18_FINDINGS.md) -------------------------------- */
/* Low: a why/enquiry answer's reasons are bullets under its heading, not one
   line of sentences joined with middots. */
.abub .ask-reasons-h { display: block; margin: 8px 0 0; font-weight: 600; }
.abub .ask-reasons { margin: 6px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; }
.abub .ask-reasons li { line-height: 1.5; }
@media (max-width: 900px) {
  /* Low: a few pixels between the AI card and the request strip on a phone
     (they sat 10px apart, the strip 40px from the next card). */
  .gtf-bar.gtf-sticky { margin-top: 12px; }
}
@media (max-width: 620px) {
  /* Low: the staff-only chat button floated over the AI card at 390. On a
     phone it docks in the header band beside the avatar and scrolls away
     with it, so it never sits over a card. (Staff only: a client never has
     #chat-fab, base.html's show_chat.) */
  body.has-pshell #chat-fab {
    position: absolute; top: 9px; bottom: auto; right: 60px;
    width: 38px; height: 38px; box-shadow: none;
  }
}
/* ---- end Phase 18 fold-in */
