/* Lead Scout.
 *
 * Light is the default; the OS preference and an explicit toggle both switch it.
 * EVERY colour is a variable — an earlier version hardcoded badge colours and
 * they turned unreadable the moment the background went white.
 *
 * The layout has one job: let someone scan a few thousand tenders and decide
 * which to open. So the deadline gets a dedicated rail, the title is the largest
 * thing on a card, and everything else stays quiet until you look for it.
 *
 * Two rules keep the polish from turning into noise:
 *   - Colour carries meaning or it is grey. The accent means "you can act on
 *     this"; the deadline ramp means "this is how much time is left". Nothing is
 *     tinted for decoration, because then neither would read as a signal.
 *   - Depth is one step. A card lifts off the page; nothing lifts off a card.
 *
 * Density is a feature, not a preference. The queue is ~3,900 tenders and half
 * of their titles run past 120 characters, so the old card put four on a screen.
 * The row below puts thirteen there. Everything in this file that looks tight is
 * tight on purpose.
 */

/* DARK IS THE DEFAULT. The product is a queue people sit in front of all day,
   and the identity we want is the near-black one. Light is a full peer, not an
   afterthought — it is simply the opt-in now, so the tokens below define dark
   and the light block overrides them. */
:root {
  color-scheme: dark;

  /* A near-black shifted slightly toward blue, so it sits under the accent
     rather than beside it. Surfaces separate by lightness; borders do very
     little work here. */
  --bg:#0b0e12; --surface:#12161c; --surface-2:#1a1f27; --surface-3:#232a34;
  /* The selected segment of a segmented control sits ABOVE its --surface-3
     track: --surface is the darkest surface here, so it read as a hole. */
  --seg-on:#2e3744; --seg-hover:#2a323d;
  --fg:#eef1f5; --fg-2:#a7b0bd; --mut:#8d96a3;
  --line:#212832; --line-2:#2d3540;
  /* Genuinely inactive, and ONLY that — see the light block for why this role
     exists apart from --surface-2. */
  --recessed:#1a1f27;

  /* A warm vermilion: the brand AND the primary action.
  
     Red is the hard case, because this app already spends red on urgency — the
     "closing today" rail, an error, a blocked bid. Two things keep them apart
     rather than letting either stop meaning anything:
  
       1. HUE. The brand leans orange; urgency below is pulled toward crimson.
          Side by side they are visibly different reds, not two shades of one.
       2. FORM. The brand is only ever a SOLID fill you can press. Every
          semantic red is a tinted chip with no affordance. Solid-versus-tint
          does more work here than hue alone.

     2026-08-22: the brand is NETFLIX RED (#e50914), a near-pure red at 357deg.
     Everything meaning "act now or this is dangerous" therefore sits in the
     WARM band well clear of it — the deadline ladder runs orange to amber to
     yellow, and --alert is amber at ~30deg, a third of a turn from the brand.
     The rule that matters is unchanged since the beginning: the brand hue and
     the alarm hues must never be neighbours. Do not let a new red drift back.

     Per-theme lightness is deliberate, not an inconsistency: true #e50914 on
     the #12161c ground measures 3.79:1, below the 4.5 floor, so the DARK theme
     carries a lifted version of the same hue and the LIGHT theme carries the
     brand exactly, where white is the ground and it measures 4.79:1.

     State chips stay de-saturated for the same reason. */
  /* Hibiscus. The DARK value is lighter than the light-theme one on purpose:
     a deep crimson on a #12161c ground measures 3.4:1 — the deep version
     lives in the light theme below, where white is the ground. Measured
     #e5537c on --surface = 5.06:1, matching the outgoing accent's 5.17. */
  /* --acc-fg measured on --acc: #2a0708 was 4.71:1, which an axe audit of
     /plans (2026-10-02) still read as 4.31:1; #140203 = 5.13:1 leaves room. */
  --acc:#f72a34; --acc-hi:#f85159; --acc-fg:#140203;
  --acc-soft:rgba(247,42,52,.13); --acc-ring:rgba(247,42,52,.34);
  --glow:rgba(247,42,52,.12);
  /* The mark's second hue — the outer rings only, never a UI action colour.
     Keeping --acc as the one interactive/brand colour means the logo can
     carry a second hue without a second "what's clickable" signal. */
  --tide:#2f8b96;

  /* Shadows exist in dark too. Without them every panel sat flat and the whole
     app read as one sheet of grey. */
  --shadow-sm:0 1px 2px rgba(0,0,0,.5);
  --shadow:0 1px 2px rgba(0,0,0,.5), 0 2px 8px -2px rgba(0,0,0,.6);
  --shadow-hi:0 2px 6px rgba(0,0,0,.5), 0 16px 34px -12px rgba(0,0,0,.8);

  /* Time remaining. The ramp is the one place colour is allowed to shout. */
  --d0:#3e1a06;  --d0fg:#ffa366;   /* closing now — WARM, NOT the brand crimson */
  --d7:#452806;  --d7fg:#ffd9ad;   /* within a week    */
  --d30:#3b3004; --d30fg:#f2e29c;  /* within a month   */
  --dfar:#1f262f; --dfarfg:#96a0ad;
  /* State chips: a tint and a readable text colour, never the brand green. */
  --ok:#0e2b1a;  --okfg:#7fd8a2;
  --warn:#3b3004; --warnfg:#f2e29c;
  --bad:#3e1a06; --badfg:#ff9a6b;
  --neutral:#1f262f; --neutralfg:#a7b0bd;
  --info:#14243d; --infofg:#9cc0ff;

  --radius:12px; --radius-sm:8px; --radius-xs:6px; --radius-pill:999px;
  --bar-h:102px;   /* brand row 58 + tab row 44 */

  /* ---- type scale -------------------------------------------------------
     Six steps with their own weight and tracking. The app used to sit between
     13 and 15px almost everywhere, which is why nothing looked more important
     than anything else. */
  --t-display:28px;   --t-display-w:700;  --t-display-ls:-.03em;
  --t-section:16.5px;                       /* real section headings */
  --t-row:14.5px;     --t-row-w:600;      --t-row-ls:-.008em;
  --t-sub:13.5px;                           /* subtitles, quiet lines */
  --t-label:10.5px;                         /* micro-caps: .lbl, .pb, th */

  /* ---- spacing scale ----------------------------------------------------
     Replaces ad-hoc margins; the same section heading was 26px in some places
     and 30px in others. */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px;

  /* Alerts need a LINE colour, not the text colour --badfg, which is a pale
     pink and read as decoration on a 3px rule. login.html had invented its own;
     it lives here now. */
  --alert:#f5842c;
}

/* Light, for anyone who wants it — same roles, same names, inverted.
   ONE selector, not a selector plus a media query, because the pre-paint script
   in layout.html resolves the theme and writes it to the attribute before the
   first frame. That is also a bug fix: <html> used to ship a hardcoded
   data-theme, so `:root:not([data-theme=...])` could never match and the OS
   preference branch was unreachable dead code. */
:root[data-theme="light"] {
  color-scheme: light;
  /* THE GREYS ARE THE WHOLE COMPLAINT, and it was a collision of meaning, not
     taste: --surface-2 painted the disabled state AND every live toolbar,
     table header and panel head, both with --mut text on top. A recessed grey
     fill under muted uppercase text IS the universal look of "switched off",
     so half the working surface of the app wore the disabled costume.

     Chrome now sits a few points off white — present enough to separate a
     header from its rows, far enough from the recessed band to read as live —
     and the separation that fill used to do is carried by --line instead,
     which is why the borders below are firmer than they were. --recessed is
     the new home for things that really are inactive, and nothing else may
     use it. White stays the hero: every card is still pure #fff. */
  --bg:#f5f7fa; --surface:#ffffff; --surface-2:#f8fafc; --surface-3:#eef2f6;
  --seg-on:#ffffff; --seg-hover:#ffffff;
  --fg:#101828; --fg-2:#344054; --mut:#667085;
  /* The ground is now near-white, so the HAIRLINE is what says "card ends
     here". It has to be firm enough to do that job alone: #fff on #fafbfd is
     a 1.03 step, invisible on its own, and a card with a timid border on a
     near-white page reads as a floating smudge rather than a panel. */
  --line:#e4e9f0; --line-2:#d0d7e2;
  /* Genuinely inactive, and ONLY that. Kept a clear step below the chrome
     above, which is what stops the two reading as the same surface. */
  --recessed:#eaeef3;
  /* Deepened for contrast on white — the dark accent would fail against it. */
  --acc:#e20812; --acc-hi:#c00710; --acc-fg:#fff;
  --acc-soft:rgba(226,8,18,.08); --acc-ring:rgba(226,8,18,.28);
  --glow:rgba(226,8,18,.10);
  /* Deepened to match, for the same reason as --acc above. */
  --tide:#1c6b73;
  --shadow-sm:0 1px 2px rgba(16,24,40,.05);
  --shadow:0 1px 2px rgba(16,24,40,.05), 0 4px 12px rgba(16,24,40,.06);
  --shadow-hi:0 6px 14px rgba(16,24,40,.10), 0 12px 28px rgba(16,24,40,.14);
  --d0:#fdf0e6;  --d0fg:#8a3a08;   /* warm, distinct from the brand crimson */
  --d7:#fdf0e0;  --d7fg:#8a4808;
  --d30:#fbf6dd; --d30fg:#665004;
  --dfar:#eef1f4; --dfarfg:#586472;
  --ok:#e6f6ec;  --okfg:#146c38;
  --warn:#fdf5d8; --warnfg:#6b5305;
  --bad:#fdf0e6; --badfg:#8a3a08;
  --neutral:#eef2f6; --neutralfg:#475467;
  --info:#eaf2ff; --infofg:#1b4fb2;
  --alert:#a8560a;
}

*, *::before, *::after { box-sizing:border-box; }
html { -webkit-text-size-adjust:100%; }

/* Inter, SELF-HOSTED (owner, 2026-09-03). One typeface on every machine is
   the single biggest "finished" signal a product gives, and the old objection
   — a download that can fail or leak a request to a third party — does not
   apply to a file served from this app's own /fonts. font-display:swap keeps
   the first paint on the system stack if the file is slow. */
@font-face {
  font-family:"Inter"; src:url("/fonts/InterVariable-2bf3d951bf9d6109437ea0874bbf12fb.woff2") format("woff2");
  font-weight:100 900; font-style:normal; font-display:swap;
}
body {
  margin:0; background:var(--bg); color:var(--fg);
  font:14px/1.55 "Inter", -apple-system, BlinkMacSystemFont, 'Segoe UI Variable Text',
       'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-feature-settings:"cv11", "ss01";
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
}
/* Form controls do NOT inherit the page font by default: every button, input
   and select rendered in the UA's Arial at 13.33px beside Inter text. Placed
   before every component rule so each explicit size below still wins. */
button, input, select, textarea { font:inherit; }
/* Tabular figures where numbers are compared down a column or tick upward, and
   nowhere else — in running prose the fixed advance makes 1 and 7 look spaced. */
.num, .n, .stat .v, .rule-stats b, .live-count b, .log .when, .dl,
.table td.num, .pcount, .fact dd {
  font-variant-numeric:tabular-nums;
}
a { color:inherit; }
h1, h2, h3 { letter-spacing:-.021em; font-weight:660; }
code, kbd, samp {
  font-family:ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
}

/* One focus treatment, everywhere, and only for keyboard users — a ring that
   also fires on click makes every button look broken after you press it. */
:focus-visible {
  outline:3px solid var(--acc-ring); outline-offset:2px; border-radius:var(--radius-xs);
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition:none !important; animation:none !important; }
}

/* ---- icons -------------------------------------------------------------- */
/* One stroke language for the whole set. Every symbol is drawn bare and
   inherits these through <use>, which is what keeps the sprite small. */
.ic {
  width:16px; height:16px; flex:none;
  fill:none; stroke:currentColor; stroke-width:1.75;
  stroke-linecap:round; stroke-linejoin:round;
  vertical-align:-.16em;
}
/* Thinner at size: a 24px stroke scaled to 40px looks clumsy. */
.ic-xl { width:38px; height:38px; stroke-width:1.25; color:var(--mut); }

/* ---- brand surface ------------------------------------------------------ */
/* A glow at the top of the page and a very faint grid behind it. This is the
   one decorative flourish in the file: it sits BEHIND the header and page title
   and never behind data, because a gradient under a table of numbers is how a
   dashboard starts to look like a poster. */
.brandbg {
  position:fixed; inset:0 0 auto 0; height:420px; z-index:0; pointer-events:none;
  background:
    radial-gradient(900px 380px at 50% -140px, var(--glow), transparent 70%);
}
/* Concentric rings spreading from the top, echoing the crosshair in our own
   logo — a range scale on an instrument. This replaces a square grid, which is
   a different product's signature and had nothing to do with ours. */
.brandbg::after {
  content:""; position:absolute; inset:0;
  background-image:
    repeating-radial-gradient(circle at 50% -40px,
      transparent 0 92px,
      color-mix(in srgb, var(--acc) 8%, transparent) 92px 93px,
      transparent 93px 186px);
  /* Fades out downward so the rings never reach the content. */
  mask-image:linear-gradient(to bottom, #000 0%, transparent 72%);
  -webkit-mask-image:linear-gradient(to bottom, #000 0%, transparent 72%);
}
.top-bar, .wrap { position:relative; z-index:1; }

/* ---- header ------------------------------------------------------------- */
/* Two rows (owner, 2026-09-03, chosen over a sidebar): brand, the global
   search and the identity on the first; every destination as a tab with room
   for a count on the second. Ten items in one 56px bar had been the loudest
   unfinished thing on every page. Solid surface, no blur: a translucent bar
   over a white page only ever looked like a rendering fault. */
.top-bar {
  position:sticky; top:0; z-index:30;
  background:var(--surface); border-bottom:1px solid var(--line);
}
.head {
  display:flex; align-items:center; gap:16px;
  max-width:1440px; margin:0 auto; padding:0 28px; min-height:58px;
}
.logo { flex:none; color:var(--fg); }
.brand {
  font-size:17px; font-weight:750; letter-spacing:-.03em;
  text-decoration:none; white-space:nowrap; margin-right:6px; margin-left:-7px;
  color:var(--fg);
}
.brand .dim { color:var(--acc); }
.tabs-row { max-width:1440px; margin:0 auto; padding:0 20px; }
.nav { display:flex; gap:2px; flex-wrap:nowrap; min-width:0; overflow-x:auto;
       scrollbar-width:none; align-items:stretch; }
.nav::-webkit-scrollbar { display:none; }
.nav a {
  position:relative; display:inline-flex; align-items:center; gap:7px;
  color:var(--fg-2); text-decoration:none;
  font-size:13.5px; font-weight:520; padding:0 10px; height:44px;
  border-bottom:2px solid transparent; margin-bottom:-1px;
  border-radius:6px 6px 0 0; white-space:nowrap;
  transition:background .14s, color .14s;
}
.nav a svg { width:16px; height:16px; opacity:.8; }
.nav a:hover { background:var(--surface-2); color:var(--fg); }
/* Where you are: the tab's own underline in the brand ink, nothing else. */
.nav a.on { color:var(--fg); font-weight:600; border-bottom-color:var(--acc); background:transparent; }
.nav a.on svg { opacity:1; color:var(--acc); }
.nav a .n {
  font-size:11.5px; color:var(--mut); background:var(--surface-3);
  border-radius:999px; padding:1px 7px; font-variant-numeric:tabular-nums;
}
/* --acc-hi, not --acc: the brand red on its own 8% tint measured 4.27:1 in
   light and 4.15:1 in dark at 11.5px (axe, live queue 2026-10-02); the
   deeper/lifted twin is 5.57:1 and 4.91:1. */
.nav a.on .n { background:var(--acc-soft); color:var(--acc-hi); }
.nav a.gap { margin-inline-start:auto; }
/* The global search lives in the brand row, compact; the same form the list
   page used to carry as a 56px pill, so the specs and the hidden filter
   inputs are unchanged. */
/* Centred in the bar (owner, 2026-09-04, matching mockup A): flex:1 grows it
   to fill the row, max-width caps it at 560px, and margin:0 auto splits the
   leftover into equal auto margins so the box floats in the middle. The
   .head-right group carries the identity to the far right. */
.head-right { display:flex; align-items:center; gap:14px; flex:none; }
.head .searchbar {
  flex:1; min-width:0; max-width:560px; margin:0 auto; display:flex; align-items:center; gap:8px;
  height:38px; padding:0 5px 0 12px; border:1px solid var(--line-2);
  border-radius:10px; background:var(--surface-2); box-shadow:none;
}
.head .searchbar:focus-within { border-color:var(--acc); box-shadow:0 0 0 3px var(--acc-ring); background:var(--surface); }
.head .searchbar .ic { color:var(--mut); flex:none; width:15px; height:15px; }
.head .searchbar input[type=search] {
  flex:1; min-width:0; border:0; background:transparent; padding:0; font-size:13.5px; box-shadow:none;
  /* A long translated placeholder ends in "…" rather than mid-word ("Dokume"). */
  text-overflow:ellipsis;
}
.head .searchbar input[type=search]::placeholder { text-overflow:ellipsis; }
.head .searchbar input[type=search]:focus { outline:none; box-shadow:none; background:transparent; }
.head .searchbar button { height:28px; padding:0 12px; font-size:12.5px; border-radius:7px; box-shadow:none; }
.head .searchbar .chip { height:26px; padding:0 9px; font-size:12px; }
/* My matches / All tenders, inside the box: a quiet native select divided
   from the field by a hairline, so it reads as part of the search. */
.head .searchbar .searchscope {
  flex:none; height:28px; padding:0 4px 0 2px; border:0; border-right:1px solid var(--line-2);
  border-radius:0; background:transparent; box-shadow:none; color:var(--fg-2);
  font-size:12.5px; font-weight:500; cursor:pointer;
}
/* ONE ring: the search box's own :focus-within ring already says the box has
   focus, so the select draws none of its own. Chrome treats a click on a
   select as :focus-visible, which stacked the global ring and select:focus
   border inside the box's ring — the "double border" (owner, 2026-09-30). */
.head .searchbar .searchscope:focus,
.head .searchbar .searchscope:focus-visible {
  outline:none; box-shadow:none; background:transparent;
  border-color:var(--line-2); color:var(--fg);
}
.spacer { flex:1; }
/* All tenders, product-less organisation: one quiet line above the results
   asking for a product, without taking the search away. */
.all-nudge { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:12px 16px;
  border-bottom:1px solid var(--line); color:var(--fg-2); font-size:13.5px; }
.all-nudge .ic { color:var(--acc); flex:none; }
.all-nudge .act { margin-left:auto; font-weight:600; }
/* The phone's stand-in for the search box's scope select (list.html). */
.scopeswitch { display:none; }
@media (max-width:720px) {
  .scopeswitch { display:flex; gap:4px; padding:4px; margin:0 0 14px; border:1px solid var(--line-2);
    border-radius:10px; background:var(--surface-2); }
  .scopeswitch a { flex:1; text-align:center; padding:8px 10px; border-radius:7px; font-size:13.5px;
    font-weight:500; color:var(--fg-2); text-decoration:none; }
  .scopeswitch a.on { background:var(--surface); color:var(--fg); font-weight:600;
    box-shadow:0 1px 2px rgba(16,24,40,.08); }
}
.inline { display:inline-flex; }

.wrap { max-width:1400px; margin:0 auto; padding:26px 28px 64px; }
@media (max-width:720px) {
  /* A phone: the search keeps its icon and field, loses its button (Enter
     submits), the wordmark yields to the mark, and the row stops trying to be
     wider than the screen — which it was, measured at 390px. */
  .head { padding:0 12px; gap:8px; }
  .head .brand { display:none; }
  .head .searchbar { flex:1 1 auto; }
  .head .searchbar button { display:none; }
  .head .searchbar .chip { display:none; }
  .head .searchbar .searchscope { display:none; }
  .tabs-row { padding:0 8px; }
  .wrap { padding:18px 16px 48px; }
}

/* ---- layout ------------------------------------------------------------- */

.layout { display:grid; grid-template-columns:268px minmax(0,1fr); gap:24px; align-items:start; }
@media (max-width:900px) { .layout { grid-template-columns:1fr; } }
/* ONE scroller on this page, the window's. The rail used to be sticky with
   its own max-height and overflow-y — a second scrollbar over the same
   screen, where the wheel did different things depending on which half the
   pointer was over, and the bottom groups hid behind a track most people
   never dragged. The detail page's facts rail made this call first; the list
   now matches it. The rail travels with the page; the script in
   fragments/filters.html adds .pin only when the whole rail fits under the
   bar, because a pinned box taller than the window strands its own bottom. */
.side {
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); padding:2px 8px 6px;
  box-shadow:var(--shadow-sm);
}
.side.pin { position:sticky; top:calc(var(--bar-h) + 14px); }
@media (max-width:900px) { .side.pin { position:static; } }

/* ---- facets ------------------------------------------------------------- */
/* Every group is a <details>: its label is the summary, so the rail is a
   short stack of headings until a group is opened. Closed, the summary also
   carries the current selection (.fhint, filled by script) so a filter that
   is shaping the list stays readable without unfolding anything. */
.fgroup { border-bottom:1px solid var(--line); }
.fgroup:last-child { border-bottom:0; }
details.fgroup > summary {
  list-style:none; display:flex; align-items:center; gap:8px;
  padding:12px 6px 12px 10px; margin:0 -2px; cursor:pointer;
  border-radius:var(--radius-xs); transition:background .12s;
}
details.fgroup > summary::-webkit-details-marker { display:none; }
details.fgroup > summary:hover { background:var(--surface-3); }
details.fgroup > summary .lbl { margin:0; flex:none; }
details.fgroup > summary .fhint {
  flex:1; min-width:0; text-align:right; font-size:12.5px; font-weight:560;
  color:var(--acc); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
details.fgroup[open] > summary .fhint { display:none; }
details.fgroup > summary .chev {
  flex:none; width:15px; height:15px; color:var(--mut);
  transition:transform .15s;
}
details.fgroup[open] > summary .chev { transform:rotate(180deg); }
.fbody { padding:0 0 10px; }
/* The first row of every group is its "any" reset. When nothing is selected
   it is the "on" row, and it used to take the same accent fill as a real
   selection — so an untouched rail showed two or three red bars saying
   nothing. The reset reads as the quiet default; the accent is kept for a
   choice someone actually made. */
.fbody > .frow.on:first-child, .fbody > form .frow.on:first-child {
  background:transparent; box-shadow:none; color:var(--fg); font-weight:600;
}
.lbl {
  display:block; color:var(--mut); font-size:var(--t-label); font-weight:700;
  text-transform:uppercase; letter-spacing:.085em; margin-bottom:8px;
}
.frow {
  display:flex; justify-content:space-between; align-items:center; gap:10px;
  padding:5px 10px; border-radius:var(--radius-xs); font-size:13.5px;
  color:var(--fg-2); text-decoration:none; line-height:1.45;
  transition:background .12s, color .12s;
}
.frow:hover { background:var(--surface-3); color:var(--fg); }
/* Selected, not shouting. A solid accent bar per selected facet made the
   sidebar louder than the list it filters, and there are three of them on
   screen at once. */
.frow.on {
  background:var(--acc-soft); color:var(--acc); font-weight:620;
  box-shadow:inset 2px 0 0 var(--acc);
}
.frow .n { color:var(--mut); flex:none; }
.frow.on .n { opacity:.85; }
.frow > span:first-child { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ---- controls ----------------------------------------------------------- */

/* One label style for every form (owner's call, 2026-09-15, GitHub-style):
   dark, semibold, reading size, above its field. Forms had two looks, small
   grey labels here and plain text in .stacked-form, so one page could show
   both. A checkbox's label is a sentence beside the box, not a field title,
   and stays regular. */
.dform label, .stacked-form label, .acct-pw label, .signin-card form label {
  display:block; font-size:13.5px; line-height:1.4; color:var(--fg);
  font-weight:600; letter-spacing:0;
}
.dform label { margin:16px 0 6px; }
.dform label.sub, .stacked-form label.inline-check,
.dform label:has(> input[type="checkbox"], > input[type="radio"]) {
  font-size:var(--t-sub); color:var(--fg-2); font-weight:400;
}
/* A control or help line inside a wrapping label keeps its own weight. */
.stacked-form label :is(input, select, textarea, .hint, .field-help, .sub) { font-weight:400; }
/* The tender filter sidebar is a compact filter, not a settings form: its date
   labels keep the look they had (their From/To text is styled by .date-range). */
.dform .date-range label.dr {
  font-size:11.5px; color:var(--mut); font-weight:600; letter-spacing:.01em; margin:14px 0 5px;
}
/* Textareas were the browser's default 20 columns while the inputs beside
   them ran the full width — the keyword box, the one field people type most
   into, was the narrowest thing on the form. */
.dform input, .dform select, .dform textarea { width:100%; }
.dform textarea { resize:vertical; min-height:68px; }

input, select, textarea {
  background:var(--bg); color:var(--fg); border:1px solid var(--line-2);
  border-radius:var(--radius-sm); padding:8px 11px;
  /* Longhand, NOT the `font:` shorthand. `font:14px/1.45 inherit` is invalid —
     the shorthand takes no `inherit` for the family — so browsers dropped the
     whole declaration and every control fell back to its own default. Inputs
     happened to look right; textareas came out monospace, which read as a
     deliberate choice nobody had made. */
  font-family:inherit; font-size:14px; line-height:1.45;
  transition:border-color .12s, box-shadow .12s, background .12s;
}
/* The phrase box IS code — the same rule is rendered as code on the tiles and in
   the editor, so it is monospace on purpose here. */
textarea[name="keywords"] {
  font-family:ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
  font-size:13px; line-height:1.6;
}
input::placeholder, textarea::placeholder { color:var(--mut); opacity:.75; }
input:hover, select:hover, textarea:hover { border-color:var(--mut); }
input:focus, select:focus, textarea:focus {
  outline:none; border-color:var(--acc); background:var(--surface);
  box-shadow:0 0 0 3px var(--acc-ring);
}
select[multiple] { padding:6px; }

button, a.btn {
  background:var(--acc); color:var(--acc-fg); border:1px solid transparent;
  border-radius:var(--radius-pill); padding:8px 16px; cursor:pointer;
  font:640 13.5px/1.35 inherit; letter-spacing:-.005em;
  box-shadow:var(--shadow-sm);
  transition:background .13s, box-shadow .13s, transform .06s;
}
button:hover { background:var(--acc-hi); box-shadow:var(--shadow); }
/* A press that moves is the cheapest possible confirmation that the click
   landed, on a page where the result is a full reload. */
button:active { transform:translateY(1px); box-shadow:var(--shadow-sm); }
/* A link that is a button (.btn on an <a>) looks and presses like one. It had
   no desktop style at all, so each such link rendered as plain text beside
   real buttons, and the overview nudge's main action looked like the weakest. */
a.btn { display:inline-flex; align-items:center; gap:6px; text-decoration:none; white-space:nowrap; }
a.btn:visited { color:var(--acc-fg); }
a.btn:hover { background:var(--acc-hi); box-shadow:var(--shadow); color:var(--acc-fg); text-decoration:none; }
a.btn:active { transform:translateY(1px); box-shadow:var(--shadow-sm); }
button.ghost, .theme {
  background:var(--surface); color:var(--fg-2);
  border:1px solid var(--line-2); font-weight:540; box-shadow:none;
}
/* The one accent-hover, grouped — this was copy-pasted at six sites. */
button.ghost:hover, .theme:hover, a.chip:hover, .pager a:hover,
.presets .chip:hover {
  border-color:var(--acc); color:var(--acc); background:var(--acc-soft);
}
.theme {
  display:inline-flex; align-items:center; gap:6px;
  border-radius:var(--radius-pill); padding:6px 13px; font-size:12.5px; cursor:pointer;
  white-space:nowrap;
}
/* Destructive. Quiet at rest so an admin page doesn't scream, unmistakable on
   approach. Existing tokens only — --alert is the line colour, --bad/--badfg
   the fill pair. */
button.danger {
  background:var(--surface); color:var(--alert);
  border:1px solid color-mix(in srgb, var(--alert) 45%, transparent);
  font-weight:560; box-shadow:none;
}
button.danger:hover { background:var(--bad); color:var(--badfg); border-color:var(--alert); }

.chip {
  display:inline-flex; align-items:center; gap:6px;
  background:var(--surface); border:1px solid var(--line-2);
  border-radius:var(--radius-pill); padding:5px 13px; font-size:12.75px; color:var(--fg-2);
  text-decoration:none; white-space:nowrap; font-weight:520;
  transition:border-color .12s, color .12s, background .12s;
}
/* ---- the tender row ----------------------------------------------------- */
/* One list, one row per tender, on four surfaces. The card this replaces spent
   ~230px to say what 52px says: a description in the source language that
   nobody could read, a button bar nobody needs until they have decided, and
   34px of padding. Twelve tenders on a screen instead of four is not cosmetic —
   it is the difference between scanning a queue and paging through one. */

.rows {
  list-style:none; margin:0; padding:0;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); box-shadow:var(--shadow-sm);
  overflow:hidden;                      /* clips the end rows' tint to the radius */
}

.row {
  display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center;
  border-bottom:1px solid var(--line);
  transition:background .12s;
}
.row:last-child { border-bottom:0; }
/* Depth stays one step, as the rule at the top of this file says: the LIST is
   the raised thing. A row only tints — twelve rows that each lift on hover
   would turn the page into a trampoline. */
.row:hover, .row:focus-within { background:var(--surface-2); }

/* 74px, not 66: the rail's second line is a DATE, and the widest honest one
   it has to set is "31 DEC 27". At 66 that string wrapped, the rail grew a
   third line, and the row it lived in stood ~40px taller than its neighbours —
   which is what turned a dense list into a ragged one.

   min-height pins the rest, and the NUMBER matters: it has to clear the tallest
   rail there is — two lines, 15px over 9.5px, plus the rail's own padding, which
   measures 56.7 — or the rail is still what sizes the row and a one-line rail
   ("no date") comes up 2.7px short of its neighbours. Measured in a browser, not
   guessed: DeadlineRailLayoutTest failed on exactly that gap at 54px. 58 clears
   it with a little headroom for a platform whose metrics round the other way,
   and costs 1px a row against the rhythm it buys. */
.row-main {
  display:grid; grid-template-columns:80px minmax(0,1fr); align-items:center;
  gap:var(--s3); min-width:0; min-height:58px; padding:7px var(--s1) 7px 0;
  text-decoration:none; color:inherit;
}
.row-main:focus-visible { outline-offset:-3px; }   /* ring stays inside the row */

/* Full height, so the urgency ramp reads as one continuous column down the left
   edge — answerable in peripheral vision before a word is read. The old floating
   chip broke that column into dashes. */
.row-dl {
  align-self:stretch;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:3px; padding:7px 4px;
  /* Title case, not small caps. "3 SEP" under "today" shouted in a column
     that is read in peripheral vision; the band colour already carries the
     urgency, so the type can simply say the date. */
  font-size:10px; font-weight:600; letter-spacing:.01em; text-transform:none;
  font-variant-numeric:tabular-nums; line-height:1.15;
  /* The rail is a COLUMN, not a cell in a table. A flat tint running edge to
     edge had no boundary of its own, so on the near-white ground it read as a
     stain that the row divider happened to cross, and the eye could not tell
     where the urgency band stopped and the title began. Two insets give it
     one: a spine in the band's OWN ink — so the ramp still reads as one
     continuous colour down the left edge, only now with an edge — and a
     hairline where it hands over to the body. Inset shadows rather than
     borders because box-sizing is border-box everywhere here: a border would
     eat 4px out of the 74px track and re-wrap the very line this is fixing. */
  box-shadow:inset 3px 0 0 color-mix(in srgb, currentColor 72%, transparent),
             inset -1px 0 0 color-mix(in srgb, currentColor 18%, transparent);
}
/* ONE LINE EACH, always. Both lines are short by construction — a day count
   and a compact date — but the rail is 74px and the date follows the reader's
   locale, so a month abbreviation somewhere is going to be longer than "Sep".
   Clipping one date is a local blemish; letting it wrap re-flows the row and
   breaks the rhythm of every row around it. */
.row-dl > span {
  max-width:100%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* "Today", "Tomorrow", "12 days", "Closed". The bundle strings are lowercase
   so they can sit mid-sentence elsewhere; the rail capitalises the first
   letter. 14px rather than 15, and the track is 80px rather than 74:
   "Tomorrow" is the widest English word the rail sets and it clipped to
   "Tomorr…" at 74 — measured, not guessed. ::first-letter needs a block
   container, hence display:block on what is already a flex item. */
.row-dl .big {
  display:block; font-size:14px; font-weight:760; letter-spacing:-.03em;
  text-transform:none; line-height:1.05;
}
.row-dl .big::first-letter { text-transform:uppercase; }
/* "not scored yet" in a 66px rail. It inherited .row-dl's uppercase, .05em
   tracking and 700 weight, so it set as three fat lines and pushed the row to
   roughly 100px tall, which is what made the discussions list look broken.
   It also carries .sub, whose 16px bottom margin is dead space here for exactly
   the reason documented at .sub itself: a flex child's margin cannot collapse.
   Both are undone for this one label rather than by weakening .row-dl, which
   the deadline pills still need. */
.row-dl .sub {
  margin:0; opacity:.8;
  font-size:9px; font-weight:600; letter-spacing:0;
  /* The one exception to the nowrap above: this is a phrase, not a date, and
     it is meant to set as two balanced lines inside the rail. */
  text-transform:none; line-height:1.2; text-wrap:balance;
  white-space:normal; overflow:visible;
}

.row-body { min-width:0; }
.row-ttl {
  display:block; font-size:var(--t-row); font-weight:var(--t-row-w);
  letter-spacing:var(--t-row-ls); line-height:1.35; color:var(--fg);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.row:hover .row-ttl { color:var(--acc); }

.row-meta {
  display:flex; align-items:center; gap:6px; margin-top:2px;
  color:var(--mut); font-size:11.75px; line-height:1.35;
  white-space:nowrap; overflow:hidden;
  /* The line ENDS, it is not guillotined. overflow:hidden alone sliced the
     last tag through the middle of a glyph right against the card edge
     ("...DMS  RECORD:", "...BPM  SOFTWARE SERVICE:"), which reads as a
     rendering fault rather than as "there is more here". A short fade says
     the same thing on purpose. Kept to 26px so a line that stops short of
     the edge is untouched. */
  -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 26px), transparent);
          mask-image:linear-gradient(to right, #000 calc(100% - 26px), transparent);
}
[dir="rtl"] .row-meta {
  -webkit-mask-image:linear-gradient(to left, #000 calc(100% - 26px), transparent);
          mask-image:linear-gradient(to left, #000 calc(100% - 26px), transparent);
}
.row-meta > * { flex:none; }
/* min-width 0 let this shrink to nothing: measured, a 711-character German
   buyer name rendered in 6px, so the column was present and said nothing. A
   floor of 7ch keeps enough to recognise a name you have seen before, and the
   title attribute on the element carries the rest. */
.row-buyer { flex:0 1 auto; min-width:7ch; overflow:hidden; text-overflow:ellipsis; }
/* promoted global below */

/* Badges end the meta line rather than interrupting it; in the card they sat
   between the buyer and the category and broke the sentence being read. Smaller
   than a standalone .pb, because twelve rows of shouting badges is the same as
   none at all. */
.row-badges { display:flex; gap:4px; margin-left:2px; }

.row-badges .pb { font-size:9px; padding:1.5px 5px; letter-spacing:.04em; }

/* ONE control per row: the state, if any, and a three-dot menu holding the
   actions. Four outlined pills on every row (Claim, Keep, Ignore, Not a
   lead) were the loudest thing on a page whose job is to let the eye run
   down titles — forty-eight buttons on a screen of twelve rows, and the
   owner said so. The actions themselves are unchanged; they live one press
   away, in the same menu the detail page already uses for "More". */
.row-acts {
  display:flex; align-items:center; gap:8px; flex:none;
  padding:0 var(--s2) 0 var(--s2);
}
.row-state { font-size:11.5px; color:var(--mut); white-space:nowrap; }

.d0 { background:var(--d0); color:var(--d0fg); }
.d7 { background:var(--d7); color:var(--d7fg); }
.d30 { background:var(--d30); color:var(--d30fg); }
.dfar { background:var(--dfar); color:var(--dfarfg); }
/* No deadline is not a band on the ramp, so it does not wear the ramp's spine:
   colour and weight here would announce an urgency that does not exist. It
   keeps the hairline only, so the column still ends in the same place. */
.dnone {
  background:var(--surface-2); color:var(--mut); text-transform:none; letter-spacing:0;
  box-shadow:inset -1px 0 0 var(--line);
}

.pb {
  display:inline-block; border-radius:5px; padding:2px 6px;
  font-size:10px; font-weight:700; letter-spacing:.03em;
  text-transform:uppercase; white-space:nowrap;
}
.pb-ok { background:var(--ok); color:var(--okfg); }
.pb-warn { background:var(--warn); color:var(--warnfg); }
.access-chip { margin-inline-start:.4em; vertical-align:middle; cursor:help; }
.apply-steps { margin:0; padding-inline-start:1.3em; display:grid; gap:.7em; }
.apply-steps li { line-height:1.5; }
/* The portal link: the site's name inside the step's sentence (owner, 2026-10-01). */
.apply-steps a.apply-open { color:var(--acc); font-weight:600; text-decoration:underline; text-underline-offset:2px; }
.apply-steps a.apply-open:hover { text-decoration-thickness:2px; }
.apply-id { display:flex; flex-wrap:wrap; align-items:center; gap:.4em; margin-top:.4em; }
.apply-card .hint { margin-top:.8em; }
/* The "translated from X" chip on list rows: informative, never loud —
   the English title is the point, the chip is only its provenance. */
.pb-lang { background:var(--neutral); color:var(--neutralfg); font-weight:500; }
/* Under a detail page's title the badge is its own line. It was a bare inline
   span between the h1 and the meta row, so it sat flush on the line below —
   and vertical margin does nothing to an inline box, hence inline-block. */
.detail h1 + .pb-lang { display:inline-block; margin:4px 0 12px; }
/* On the detail page the chip is a button that swaps in the original title. */
button.pb-lang { border:0; cursor:pointer; font-family:inherit; }
button.pb-lang[aria-pressed="true"] { background:var(--info); color:var(--infofg); }
button.pb-lang:focus-visible { outline:2px solid var(--acc); outline-offset:2px; }
.pb-bad { background:var(--bad); color:var(--badfg); }
.pb-mut { background:var(--neutral); color:var(--neutralfg); }

@media (max-width:720px) {
  .row { grid-template-columns:minmax(0,1fr); }
  .row-acts { padding:0 var(--s3) 6px 92px; }   /* aligns under the 80px rail + 12px gap */
  .row-ttl, .row-meta { white-space:normal; }
}

/* ---- discussion rows ---------------------------------------------------- */
/* A forum post is not a tender, so it does not wear a tender's shape. No
   deadline rail down the left: the countdown column is the tender list's whole
   organising idea and there is nothing to count down to here. What replaces it
   is a dateline ABOVE the title — community and age, the two facts that decide
   whether a post is still worth answering — and the score moved to the right
   where the eye already goes for the number the list is sorted by. */

.talk-main {
  grid-template-columns:minmax(0,1fr) auto;   /* overrides .row-main's 66px rail */
  align-items:start;
  padding:10px var(--s1) 10px var(--s3);
}
.talk-text { display:flex; flex-direction:column; gap:3px; min-width:0; }

.talk-head {
  display:flex; align-items:center; gap:5px; flex-wrap:wrap;
  font-size:11px; color:var(--mut); line-height:1.3;
}
.talk-where { font-weight:700; color:var(--fg-2); }
.talk-age { font-variant-numeric:tabular-nums; }
/* The handle is a person, so it gets the monospace treatment a handle earns
   without becoming a third weight competing with the title. */
.talk-by {
  font-family:ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
  font-size:10.5px; color:var(--mut);
}
.talk-head .pb { font-size:9px; padding:1px 5px; }

.talk-ttl {
  font-size:var(--t-row); font-weight:var(--t-row-w); letter-spacing:var(--t-row-ls);
  line-height:1.35; color:var(--fg);
}
.row:hover .talk-ttl { color:var(--acc); }

/* Their opening words, clamped rather than truncated server-side: the full text
   stays in the DOM for search and for a screen reader. Two lines is the most
   that can be spent per row before the list stops being scannable. */
.talk-snip {
  font-size:11.5px; line-height:1.45; color:var(--mut);
  display:-webkit-box; -webkit-line-clamp:2; line-clamp:2;
  -webkit-box-orient:vertical; overflow:hidden;
  overflow-wrap:anywhere;
}
.talk-tags { display:flex; gap:4px; flex-wrap:wrap; margin-top:1px; }
.talk-tags .pb { font-size:9px; padding:1.5px 5px; letter-spacing:.04em; }

.talk-rail {
  display:flex; flex-direction:column; align-items:flex-end; gap:4px;
  width:104px; flex:none; padding-top:1px;
}
.score-pill {
  display:inline-flex; align-items:center; justify-content:center;
  min-width:38px; padding:3px 8px; border-radius:999px;
  font-size:13px; font-weight:760; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
}
.sc-hi  { background:var(--ok);   color:var(--okfg); }
.sc-mid { background:var(--warn); color:var(--warnfg); }
.sc-lo  { background:var(--neutral); color:var(--neutralfg); }
/* Unscored is a STATE, not a low score, so it must not read as one: no pill
   colour, no number shape, and the same 9px non-uppercase treatment the
   deadline rail's version needed for exactly the same reason. */
.sc-none {
  background:none; padding:0; min-width:0; text-align:right;
  font-size:9px; font-weight:600; line-height:1.2; letter-spacing:0;
  color:var(--mut); text-transform:none; text-wrap:balance;
}
.intent-pb { font-size:9px; padding:1.5px 5px; letter-spacing:.03em; }
/* A social lead the AI has not checked yet: a visible label, not a score. */
.talk-unchecked { font-size:10px; padding:1.5px 6px; letter-spacing:0; text-transform:none;
  white-space:nowrap; }

@media (max-width:720px) {
  .talk-main { grid-template-columns:minmax(0,1fr); gap:6px; }
  /* Below the text, not beside it: at this width a 104px column takes a third
     of the row and the title wraps to four lines to pay for it. */
  .talk-rail {
    width:auto; flex-direction:row; align-items:center; gap:6px;
    grid-column:1; padding-top:2px;
  }
  .talk-snip { -webkit-line-clamp:3; line-clamp:3; }
}

/* ---- utilities ---------------------------------------------------------- */
/* Each of these existed already as a repeated inline style. `color:var(--mut)`
   was written out six times on table cells; the card chrome three times, once
   without the shadow its siblings had; the same section heading with two
   different margins. */

.grow { flex:1; min-width:0; }
.sep { opacity:1; }
/* Labels wrapping their controls (the add-feed form). Lived as a <style>
   block inside org-sources' <main> for a while — invalid there, reusable here. */
/* The form is a vertical stack with real gaps between fields, and it does not
   run the width of a 1400px page. This used to be layout-by-accident: labels
   were block but the form had no column flow, no spacing and no textarea rule,
   so a form built with class="stack" (a name with NO css at all) collapsed
   every label and control onto one line — measured on /answers/new, where
   Topic, question, answer, evidence and review-date all rendered side by side.
   The three forms that used the wrong name now use this one; textarea, gap and
   a readable max-width close the gaps this rule itself was missing. */
.stacked-form { display:flex; flex-direction:column; gap:16px; max-width:620px; }
.stacked-form label { display:block; }
.stacked-form label > input,
.stacked-form label > select,
.stacked-form label > textarea { display:block; width:100%; margin-top:5px; }
.stacked-form label > textarea { min-height:200px; resize:vertical; line-height:1.5; }
.stacked-form label.inline-check { display:flex; gap:8px; align-items:center; }
.stacked-form label.inline-check > input { display:inline; width:auto; margin-top:0; }
/* The hint sits under its control, not inline beside the next field. */
.stacked-form label > .hint, .stacked-form > .hint { display:block; margin-top:6px; }

/* The sticky footer of a long form: Save travels with you. */
.form-actions {
  position:sticky; bottom:0; margin-top:18px; padding:12px 0;
  background:color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter:blur(8px); border-top:1px solid var(--line);
}

/* The first-press explanation dialog, and the confirm that replaced
   window.confirm(). Native <dialog>; ::backdrop dims. One shell, so the two
   modals in the app cannot drift apart. */
#explain-dialog, #confirm-dialog, #progress-dialog, #plan-dialog {
  background:var(--surface-2); color:var(--fg);
  border:1px solid var(--line-2); border-radius:var(--radius);
  box-shadow:var(--shadow-hi); padding:22px 24px; max-width:420px;
}
#confirm-dialog { max-width:440px; }
#plan-dialog { max-width:480px; width:calc(100vw - 32px); }
#plan-dialog h3 { font-size:18px; margin-bottom:16px; }
#plan-dialog .pd-plans { display:grid; grid-template-columns:1fr auto 1fr; align-items:stretch;
  gap:10px; margin:0 0 12px; }
#plan-dialog .pd-plan { display:flex; flex-direction:column; gap:2px; padding:12px 14px;
  border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface); min-width:0; }
#plan-dialog .pd-plan b { font-size:15px; color:var(--fg); }
#plan-dialog .pd-plan span:last-child { font-size:13px; color:var(--fg-2); }
#plan-dialog .pd-from { opacity:.7; }
#plan-dialog .pd-to { border-color:var(--acc); box-shadow:0 0 0 1px var(--acc) inset; }
#plan-dialog .pd-cap { font-size:10.5px; font-weight:700; letter-spacing:.08em;
  text-transform:uppercase; color:var(--mut); }
#plan-dialog .pd-arrow { align-self:center; color:var(--mut); font-size:18px; }
#plan-dialog .pd-save { margin:0 0 14px; font-size:13px; font-weight:600;
  color:var(--okfg); background:var(--ok); border-radius:var(--radius-sm); padding:6px 10px; }
#plan-dialog table { width:100%; border-collapse:collapse; font-size:13.5px; margin:0 0 12px; }
#plan-dialog table td { padding:6px 0; color:var(--fg-2); }
#plan-dialog table td:last-child { text-align:end; white-space:nowrap; color:var(--fg);
  font-variant-numeric:tabular-nums; }
#plan-dialog .pd-limits caption { text-align:start; font-size:12px; color:var(--mut); padding:0 0 4px; }
#plan-dialog .pd-old { color:var(--mut); text-decoration:line-through; }
#plan-dialog .pd-receipt tr + tr td { border-top:1px solid var(--line); }
#plan-dialog .pd-total td { font-weight:700; color:var(--fg); font-size:15px; padding-top:9px; }
#plan-dialog .pd-renew { margin:0 0 4px; color:var(--fg-2); font-size:13px; }
#plan-dialog .pd-fine { margin:0 0 16px; color:var(--mut); font-size:12px; }
/* When the change lands: the one fact the dialog exists to say (changes wait
   for the period end, like Slack, Notion and Linear downgrades). */
#plan-dialog .pd-when { display:flex; flex-direction:column; gap:3px; margin:0 0 14px;
  padding:10px 12px; border-radius:var(--radius-sm); background:var(--surface);
  border-inline-start:3px solid var(--acc); font-size:13px; color:var(--fg-2); }
#plan-dialog .pd-when b { color:var(--fg); font-size:13.5px; }
@media (max-width:420px) {
  #plan-dialog .pd-plans { grid-template-columns:1fr; }
  #plan-dialog .pd-arrow { transform:rotate(90deg); justify-self:center; }
}
#explain-dialog::backdrop, #confirm-dialog::backdrop, #progress-dialog::backdrop, #plan-dialog::backdrop {
  background:rgba(0,0,0,.55); backdrop-filter:blur(2px); }
#explain-dialog h3, #confirm-dialog h3, #progress-dialog h3, #plan-dialog h3 {
  margin:0 0 8px; font-size:16.5px; letter-spacing:-.015em; text-wrap:balance; }
#explain-dialog p, #confirm-dialog p, #progress-dialog p, #plan-dialog p {
  margin:0 0 14px; color:var(--fg-2); font-size:13.5px; line-height:1.6; }
/* No consequence sentence to show ("Delete your account permanently?") — the
   empty paragraph must not leave a gap above the buttons. */
#confirm-dialog p:empty { display:none; }
#confirm-dialog[open] { animation:dlg-in .13s ease-out; }
@keyframes dlg-in { from { opacity:0; transform:translateY(4px) scale(.99); } }
@media (prefers-reduced-motion:reduce) { #confirm-dialog[open] { animation:none; } }
.explain-check { display:flex; align-items:center; gap:8px;
  color:var(--mut); font-size:12.75px; margin-bottom:16px; cursor:pointer; }
.explain-check input { width:auto; margin:0; }
.explain-actions { display:flex; gap:8px; justify-content:flex-end; }

/* A folded administration section: the summary is the whole resting UI. */
.admin-fold { margin-top:32px; }
.admin-fold > summary { cursor:pointer; list-style:none; display:inline-flex;
  align-items:center; gap:8px; font-size:var(--t-section); font-weight:660;
  letter-spacing:-.015em; color:var(--fg); padding:4px 0; }
.admin-fold > summary::-webkit-details-marker { display:none; }
.admin-fold > summary::before { content:"▸"; color:var(--mut); font-size:12px;
  transition:transform .12s; }
.admin-fold[open] > summary::before { transform:rotate(90deg); }
.admin-fold > summary:hover { color:var(--acc); }

/* The /platform console's five admin sections, folded. .pfold only tunes the
   RUN of five built on .admin-fold: a rule between each instead of a 32px gap
   before each (right once, too loud five times), and an <h3> inside the
   <summary> that inherits the summary's own type so the page keeps a real
   heading outline while looking identical open or shut. */
.pfold { margin-top:0; padding-top:14px; border-top:1px solid var(--line); }
.pfold:first-of-type { margin-top:24px; }
.pfold > summary h3 { margin:0; font:inherit; letter-spacing:inherit; }

/* The bare × that deletes a row item (saved searches). A button, but quiet
   until it is pointed at — and alert-coloured then, because it destroys. */
.x-del { background:transparent; border:0; box-shadow:none; padding:0 4px;
         color:var(--mut); font-size:15px; line-height:1; cursor:pointer; }
.x-del:hover { color:var(--alert); background:transparent; }

/* One flex action row — replaces ~15 inline display:flex declarations. */
.actions { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin:0 0 var(--s4); }
.actions .spacer { flex:1; }
/* A chip wider than a phone (a long product name on /expiry measured 398px at a
   390px viewport) pushed the whole page sideways. On a narrow screen it wraps
   inside its own pill instead; nowrap stays for everything that fits. */
@media (max-width: 620px) {
  .actions .chip { white-space:normal; max-width:100%; min-width:0; overflow-wrap:anywhere; }
}
.dform .actions { margin:10px 0 0; }
.dform .actions button { flex:1; }
.actions select { padding:5px 10px; font-size:13px; }
.actions button, .section-head button { padding:6px 14px; }

/* Real tabs. Chips were doing this job, styled identically to filters and
   back-links — three meanings, one look. */
.tabs { display:flex; gap:4px; margin:0 0 6px; border-bottom:1px solid var(--line); }
.tab { padding:8px 14px; font-size:var(--t-sub); font-weight:560; color:var(--fg-2);
       text-decoration:none; border-bottom:2px solid transparent; margin-bottom:-1px; }
.tab:hover { color:var(--fg); background:var(--surface-2); }
.tab.on { color:var(--acc); border-bottom-color:var(--acc); font-weight:640; }
/* .tabs is SHARED — the platform org-detail six-tab strip AND pipeline's
   (/mine, /not-a-lead, /ignored). It had no wrap and no overflow and no narrow
   override, so the six-tab strip pushed /platform/orgs/{id} ~154px past a 390px
   viewport (measured 544). Contain the run WITHIN the strip, the .table-scroll
   way, so it scrolls itself instead of sliding the page; pipeline's shorter
   strip is unaffected but no longer relies on fitting by luck. Not .tabs-row —
   that is the main app nav and already has its own narrow treatment. */
@media (max-width:620px) {
  .tabs { overflow-x:auto; -webkit-overflow-scrolling:touch; }
  .tabs .tab { white-space:nowrap; }
}

/* A quiet one-line empty state. The 52px dashed .empty monument is reserved
   for a whole PAGE with nothing to show; a section with nothing to say gets a
   sentence. */
.empty-line { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
              color:var(--mut); font-size:var(--t-sub); padding:6px 2px; }
.empty-line b { color:var(--fg-2); font-weight:620; }

/* Phone-safe tables: the page must never scroll sideways because a table did. */
/* max-width and min-width are what make the scroller actually contain its
   table. overflow-x:auto alone only promises to scroll IF the box is
   constrained — inside a flex or grid parent the wrapper happily sizes to its
   content instead, and the page slides anyway. Measured on /legal/privacy:
   wrapping the table alone took it from 458px to 406px in a 390px viewport;
   these two lines took it to 390. */
.table-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; max-width:100%; min-width:0;
  /* The containing block for what a table hides for screen readers (.sr-only
     is position:absolute): without it the label escaped the scroller's clip
     and widened the page, /expiry at 768 scrolling to 811 (2026-10-02). */
  position:relative; }

/* ---- MOBILE SAFETY NET -------------------------------------------------
   Three rules that stop a page sliding sideways under the thumb. Each was
   measured at 390px before being written, not guessed.

   Inline code: a keyword string like govtech+procurement+sysadmin has no
   space to break at, so /org/sources pushed the document to 523px. Long
   words in code break anywhere now — a scrollbar or a clipped page are both
   worse than a mid-token wrap in a technical string.

   The help panel on /feedback: its summary and answers are long sentences
   inside a flex hint box, and without an explicit min-width:0 a flex child
   refuses to shrink below its content, so the page went to 468px.

   Long unbroken strings anywhere in prose — a URL, an org name, a keyword —
   get the same treatment, because the next one of these will not be in a
   place anybody predicted. */
code, .mono, kbd, samp { overflow-wrap:anywhere; }
.hint, .hint > *, details > summary, details > p { min-width:0; overflow-wrap:anywhere; }
.leaving-msg, .row-buyer, .talk-where { overflow-wrap:anywhere; }
/* A <select> sizes itself to its WIDEST OPTION, and the role picker on
   /org/members carries a full sentence ("Member. Claims and works tenders...")
   — 416px of it in a 390px viewport. Form controls never exceed their column
   now; the option text still reads in full once the menu is open, which is
   where a person actually reads it. */
select, input, textarea, button { max-width:100%; }

.navburger, .railburger { display:none; }   /* phone-only; the media query below turns them on */

/* ---- BREAKPOINTS -------------------------------------------------------
   Three widths, used for everything new. Custom properties cannot appear in
   a media query, so the constants live here as documentation and the numbers
   are repeated literally — the point is that there are THREE of them, not the
   six that had accumulated (520, 560, 620, 640, 720, 900) and drifted apart.

     phone   <= 620px   one column, touch sizing, rails become drawers
     tablet  <= 900px   the filter rail folds, the grid stays
     desktop  > 900px   the full two-column layout

   Existing rules at other widths are left where they are: rewriting fifteen
   working media queries to prove a point risks regressions nobody asked for.
   New work uses these three.

   ---- TOUCH TARGETS -----------------------------------------------------
   Apple and Android both ask for ~44px; measured on this app the interactive
   controls were 21-32px: row-action summaries at 32, filter chips at 32,
   per-page buttons at 30, chip buttons at 29, the brand at 28 and the "show
   them" action at 21. Desktop density is untouched — this only applies where
   a thumb is the pointer. min-height rather than padding so the label stays
   put and only the hit area grows. */
@media (max-width:620px) {
  .chip, .sortbar-opt, .sortbar-size, .row-acts a, .row-acts button,
  .pmenu > summary, details > summary, .act, .brand,
  .facet a, .frow, .pager a, .tab, .seg-btn {
    min-height:44px;
    display:inline-flex; align-items:center;
  }
  /* Rows in the filter rail are full-width targets, not inline ones. */
  .facet a, .frow { display:flex; width:100%; }
  /* Inputs and selects are touched too. */
  input:not([type=checkbox]):not([type=radio]), select, textarea, button[type=submit] {
    min-height:44px;
  }
  /* Controls that carry a class but were missed by the list above: a product
     tile's name link at 24px, a plain .btn at 22px, a source link at 20px. */
  .tile-name, .btn, .site-link, .row-main { min-height:44px; display:flex; align-items:center; }
  /* Menu items and the solid/secondary/ghost buttons sat at 35-36px — close
     enough to look fine and still short for a thumb. */
  .pmenu-item, .pmenu-panel a, .btn-solid, .btn-sec, .ghost, .danger {
    min-height:44px; display:inline-flex; align-items:center;
  }
  .pmenu-item, .pmenu-panel a { width:100%; }

  /* ---- THE FILTER RAIL BECOMES A DRAWER -------------------------------
     The rail is a 268px sidebar. On a 390px screen that is two thirds of the
     width spent on controls for results the reader cannot see, and the grid
     already stacks it ABOVE the list, so every visit starts with a screenful
     of filters before a single tender. Collapsed behind its own toggle, the
     results come first and the filters are one tap away. */
  .layout > aside, .layout > .side { width:100%; position:static; max-height:none; }
  .railtoggle { display:none; }
  .railburger {
    display:inline-flex; align-items:center; gap:8px; min-height:44px; width:100%;
    padding:0 12px; margin:0 0 12px; cursor:pointer; user-select:none;
    font-size:14px; font-weight:600; color:var(--fg-2);
    background:var(--surface-2); border:1px solid var(--line); border-radius:8px;
  }
  .railtoggle:not(:checked) ~ .side, .railtoggle:not(:checked) ~ aside { display:none; }
  .railtoggle:focus-visible + .railburger { outline:2px solid var(--acc); outline-offset:2px; }

  /* ---- THE NAV BECOMES A MENU ----------------------------------------
     Eleven destinations scrolled sideways here, which is reachable in theory
     and invisible in practice: a person sees three and has no cue the other
     eight exist. Closed by default on a phone, opened by the toggle, and each
     destination becomes a full-width row rather than a chip in a strip. */
  .navburger {
    display:inline-flex; align-items:center; gap:8px; min-height:44px;
    padding:0 12px; margin:4px 0; cursor:pointer; user-select:none;
    font-size:14px; font-weight:600; color:var(--fg-2);
    background:var(--surface-2); border:1px solid var(--line); border-radius:8px;
  }
  .navburger:active { background:var(--surface-3); }
  .navtoggle:focus-visible + .navburger { outline:2px solid var(--acc); outline-offset:2px; }
  .nav { display:none; flex-direction:column; width:100%; overflow-x:visible; padding-bottom:6px; }
  .navtoggle:checked ~ .nav { display:flex; }
  .nav a { width:100%; min-height:44px; border-radius:8px; }
  /* The active destination stays visible when the menu is shut, so the person
     can see where they are without opening anything. */
  /* The active marker is a BOTTOM border, drawn for a horizontal strip. In a
     vertical menu it became a stray rule across the full width under the
     current item. A tinted row with a leading edge says the same thing in a
     list. */
  .nav a.on { border-bottom-color:transparent; background:var(--acc-soft);
              box-shadow:inset 3px 0 var(--acc); }
  /* Shut, the menu shows NOTHING. Repeating the current destination here cost
     44px to say what the heading two inches below already says. */
  .navtoggle:not(:checked) ~ .nav { display:none; }
  .tabs-row { padding-top:0; padding-bottom:0; }

  /* ---- DENSITY -------------------------------------------------------
     Measured on a 390x844 phone, 709px of chrome stood between the top of the
     screen and the first tender — 84% of the display, so the product's actual
     content began below the fold. Every rule below buys some of that back.

     The toolbar wrapped to three lines once its controls became 44px. One
     scrollable row instead: the labels go (the chips say "Closing soonest",
     they do not need "SORT BY" in front of them), and per-page follows the
     sort chips in the same strip. 133px -> 44px. */
  .sortbar { flex-wrap:nowrap; overflow-x:auto; scrollbar-width:none; row-gap:0; }
  .sortbar::-webkit-scrollbar { display:none; }
  .sortbar > .sortbar-lbl { display:none; }
  .sortbar-right { margin-inline-start:8px; }
  .sortbar-right .sortbar-lbl { display:none; }
  .sortbar .seg, .sortbar-seg { flex:none; }

  /* The head: the actions sit under a long title on a phone anyway, so the
     stack does not need desktop's breathing room. */
  .page-head { gap:8px; }
  .page-head-row { padding-bottom:4px; }
  .page-actions { flex-wrap:wrap; }
  /* A checkbox stays its size but gains a bigger reachable label. */
  label:has(> input[type=checkbox]), label:has(> input[type=radio]) {
    min-height:44px; display:inline-flex; align-items:center; gap:8px;
  }
}

/* Round ⓘ — the explainer trigger and any "what is this" affordance. */
.info-btn { display:inline-flex; align-items:center; justify-content:center;
            width:26px; height:26px; padding:0; border-radius:50%; flex:none;
            background:transparent; color:var(--mut); border:1px solid var(--line-2);
            cursor:pointer; box-shadow:none; }
.info-btn:hover { color:var(--acc); border-color:var(--acc); background:var(--acc-soft); }
.info-btn .ic { width:14px; height:14px; }

/* A muted sub-line under a value. Was an inline style, five times. */
.kv-note { display:block; color:var(--mut); font-size:12.5px; margin-top:2px; line-height:1.5; }

/* The quiet identity line under a page or detail title. */
.meta { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
        color:var(--mut); font-size:13px; }

/* Standalone-page lede (login/share) — was defined twice, at two sizes. */
.lede { color:var(--mut); font-size:var(--t-sub); line-height:1.55; margin:0 0 20px; }

/* The panel that forms and rule blocks sit on. */
.surface {
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); padding:18px 20px; box-shadow:var(--shadow-sm);
}

/* A real section heading: h2 is the page, .h-section is a section, .lbl is a
   sidebar micro-label. The three were one class for a while, which is why every
   heading rendered 10.5px grey caps — SMALLER than the prose it introduced. */
.h-section {
  font-size:var(--t-section); font-weight:660; letter-spacing:-.015em;
  line-height:1.35; color:var(--fg); margin:var(--s6) 0 10px;
}
.h-section:first-child { margin-top:0; }
.h-sub { margin:0 0 12px; color:var(--mut); font-size:var(--t-sub); line-height:1.5; }

/* Help text under a control. It was an inline margin:-6px hack repeated five
   times, pulling the sentence UP into the border of the field above it — which
   is exactly how it looked once the fields were centred and there was nothing
   else to blame. The control already owns the gap; this just sits in it. */
.field-help {
  margin:2px 0 var(--s3); color:var(--mut); font-size:12.5px; line-height:1.5;
}

/* A page that is one column of form. Centred, heading and all: a 720px panel
   left-pinned in a 1400px page leaves half the screen empty beside it and reads
   as a layout that failed to finish. The heading travels with the column,
   because a centred form under a left-aligned title is worse than either. */
.form-page { max-width:760px; margin-inline:auto; }

/* ---- page furniture ----------------------------------------------------- */

.sub { color:var(--mut); font-size:var(--t-sub); margin:0 0 var(--s4); }
/* .sub carries a 16px bottom margin, which is right for a standalone subtitle
   and WRONG wherever it is a flex child: a flex item establishes its own
   formatting context, so the margin cannot collapse and becomes dead space
   inside the box. It cost 6.6px of logo misalignment on /org and 16px of
   lopsided padding at the foot of every product tile. Reset it where .sub is
   a layout child. */
.org-id-meta, .tile-foot .sub, .tile-head .sub, .section-head .sub { margin-bottom:0; }
.sub b { color:var(--fg); font-weight:640; }
.page-head { margin-bottom:var(--s5); }
.page-head h1, .page-head h2 {
  font-size:var(--t-display); font-weight:var(--t-display-w);
  letter-spacing:var(--t-display-ls); margin:0 0 var(--s1);
}

.pager {
  display:flex; gap:6px; align-items:center; justify-content:center;
  margin:28px 0 0; flex-wrap:wrap;
}
.pager a {
  padding:7px 14px; border:1px solid var(--line-2); border-radius:var(--radius-pill);
  text-decoration:none; font-size:13.5px; font-weight:540; color:var(--fg-2);
  background:var(--surface); transition:border-color .12s, color .12s, background .12s;
}
.pager a.cur {
  background:var(--acc); color:var(--acc-fg); border-color:var(--acc);
  font-weight:660; box-shadow:var(--shadow-sm);
}
.pcount { color:var(--mut); font-size:12.5px; margin-left:10px; }

.empty {
  color:var(--mut); text-align:center; padding:52px 24px;
  background:var(--surface); border:1px dashed var(--line-2);
  border-radius:var(--radius); font-size:13.5px;
}
.empty b { color:var(--fg); display:block; margin-bottom:6px; font-size:15px; }
.empty .ic-xl { display:block; margin:0 auto var(--s3); }

.hint {
  background:var(--acc-soft); border:1px solid transparent;
  border-left:3px solid var(--acc);
  border-radius:var(--radius-sm); padding:12px 15px; margin:0 0 16px;
  font-size:13.5px; color:var(--fg-2); line-height:1.6;
}
.hint b { color:var(--fg); }
.hint.err { background:var(--bad); color:var(--badfg); border-left-color:var(--alert); }
/* --badfg is a foreground for a tinted chip; as bold text on the alert
   panel it washed out. The alert already carries the colour. */
.hint.err b { color:var(--fg); }

/* ---- detail page -------------------------------------------------------- */

/* ---- detail page: reading column + facts rail --------------------------- */
.detail {
  display:grid; grid-template-columns:minmax(0,1fr) 320px;
  grid-template-areas:"head head" "main facts";
  gap:20px; align-items:start;
  max-width:1180px; margin-inline:auto;
}
/* The head is a full-width HERO CARD (mockup A detail): title, meta and actions
   read as one panel; the content and facts sit in two columns below it. */
.detail-head { grid-area:head; min-width:0;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--radius);
  padding:20px 24px; box-shadow:var(--shadow-sm); }
.detail-main { grid-area:main; min-width:0; }

.detail h1 {
  font-size:var(--t-display); font-weight:var(--t-display-w);
  letter-spacing:var(--t-display-ls); line-height:1.26; margin:10px 0 8px;
}
.detail .body {
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); padding:20px 22px; box-shadow:var(--shadow-sm);
  white-space:pre-wrap; line-height:1.7; font-size:15.5px; color:var(--fg-2);
  /* Sources ship notice text as ONE long line, and with no measure it set to
     the full width of the column — about 110 characters, roughly double what
     is comfortable to read. The card still fills the column; the text inside
     it does not. */
  overflow-wrap:anywhere;
}
.detail .body > * , .detail .body { max-width:82ch; }
/* Markdown-rendered notice text. The plain body uses pre-wrap so a label-per-
   line field block keeps its breaks; rendered markdown has real <p>/<li> block
   structure, so pre-wrap would double-space every element. normal lets the
   markup own the spacing, and the margins give headings/lists room to breathe. */
.detail .body-md { white-space:normal; }
.detail .body-md p { margin:0 0 .85em; }
.detail .body-md p:last-child { margin-bottom:0; }
.detail .body-md ul, .detail .body-md ol { margin:.25em 0 .85em; padding-left:1.4em; }
.detail .body-md li { margin:.25em 0; }
.detail .body-md h3, .detail .body-md h4, .detail .body-md h5, .detail .body-md h6 {
  color:var(--fg); font-weight:650; margin:1.1em 0 .45em; line-height:1.3; }
.detail .body-md h3 { font-size:16.5px; }
.detail .body-md h4 { font-size:15.5px; }
.detail .body-md h5, .detail .body-md h6 { font-size:14.5px; }
.detail .body-md a { color:var(--acc); }
.detail .body-md strong { color:var(--fg); font-weight:650; }

.actionbar { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin-top:14px; padding-top:14px; border-top:1px solid var(--line); }
.actionbar select { padding:7px 12px; font-size:13.5px;
  border-radius:var(--radius-pill); }
.status-note { color:var(--fg-2); font-size:13px; }


.explainer-i { position:relative; margin:0 0 0 auto; }
.explainer-i dl { position:absolute; right:0; top:calc(100% + 8px); z-index:40;
  width:min(560px, calc(100vw - 40px)); margin:0;
  background:var(--surface-2); border-color:var(--line-2);
  box-shadow:var(--shadow-hi); }

/* ONE scroller on this page, and it is the window's. The rail used to be
   sticky with its own max-height and overflow-y — a second scrollbar over the
   same screen, where the wheel does different things depending on which half
   the pointer is over, and the rail's own bottom rows hide behind a track most
   people never think to drag.

   The two cannot both exist. A rail TALLER than the viewport — and this one is:
   closing date, assignee, stage, actions, AI match, value, viability, outcome,
   documents — either scrolls inside itself or travels with the page, and a
   pinned box taller than the screen strands its own bottom forever, because
   sticky stops moving exactly when the part you have not read is off-screen.
   So it travels, which is what the <=900px layout already did. */
.facts { grid-area:facts;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); padding:4px 18px 14px;
  box-shadow:var(--shadow-sm); }
.facts dl { margin:0; }
.fact { padding:11px 0; border-bottom:1px solid var(--line); }
.fact:last-child { border-bottom:0; }
.fact-contact .contact-line { display:block; overflow-wrap:anywhere; }
.fact-contact .contact-name { font-weight:600; }
.fact dt { color:var(--mut); font-size:var(--t-label); font-weight:700;
  text-transform:uppercase; letter-spacing:.07em; margin-bottom:3px; }
.fact dd { margin:0; font-size:14px; color:var(--fg); }
.fact-hero dd b { font-size:15.5px; letter-spacing:-.01em; margin-right:6px; }
.fact-link { display:flex; align-items:center; justify-content:space-between;
  gap:8px; min-width:0; padding:4px 0; color:var(--fg-2);
  text-decoration:none; font-size:13px; }
.fact-link > span:first-child { overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }
.fact-link:hover { color:var(--acc); }
.info-tip { display:inline-flex; align-items:center; justify-content:center;
  width:16px; height:16px; margin-left:4px; border-radius:50%;
  border:1px solid var(--line-2); color:var(--mut);
  font-size:10px; font-weight:700; font-style:normal; cursor:help; }

.doc-card { padding:14px 16px; }
.doc-files { display:flex; flex-wrap:wrap; gap:8px; margin:0 0 12px; }
.doc-upload { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }

/* ---- documents: one row per file, a ⋯ menu each ------------------------- */
.doc-list { list-style:none; margin:0 0 12px; padding:0; display:flex; flex-direction:column; }
.doc-row {
  display:flex; align-items:center; gap:12px; padding:9px 4px;
  border-bottom:1px solid var(--line);
}
.doc-row:last-child { border-bottom:0; }
.doc-kind {
  flex:none; width:42px; height:30px; display:inline-flex; align-items:center; justify-content:center;
  border-radius:6px; background:var(--surface-3); color:var(--fg-2);
  font-size:10.5px; font-weight:700; letter-spacing:.04em;
}
.doc-main { display:flex; flex-direction:column; gap:1px; min-width:0; flex:1 1 auto; }
.doc-name {
  color:var(--fg); font-weight:600; text-decoration:none;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.doc-name:hover { text-decoration:underline; }
.doc-meta { color:var(--mut); font-size:12.5px; }
.docmenu { position:relative; flex:none; }
.docmenu > summary {
  display:inline-flex; align-items:center; justify-content:center;
  width:30px; height:30px; border-radius:var(--radius-sm); cursor:pointer; list-style:none;
  border:1px solid transparent; color:var(--mut);
}
.docmenu > summary::-webkit-details-marker { display:none; }
.docmenu > summary .ic { width:16px; height:16px; }
.docmenu > summary:hover, .docmenu[open] > summary {
  color:var(--fg); background:var(--surface-3); border-color:var(--line-2);
}
.docmenu[open] > summary .ic { transform:none; }
.docmenu .pmenu-panel { top:calc(100% + 4px); min-width:168px; padding:5px; }
.docmenu .pmenu-item { font-size:13px; padding:8px 11px; }
.pmenu-item.danger { color:var(--alert); background:transparent; border:0; }
.pmenu-item.danger:hover { background:var(--bad); color:var(--badfg); }
.doc-limits { margin:8px 0 0; color:var(--mut); font-size:12px; }
.doc-full { color:var(--fg-2); }

/* ---- notes: a comment thread --------------------------------------------- */
/* The thread spans the column like the cards above it: capped at 82ch it
   ended 116px short of them, a second right edge in one column. */
.note-thread { display:flex; flex-direction:column; gap:12px; }
.note-card, .note-composer { display:flex; align-items:flex-start; gap:10px; }
.note-avatar {
  flex:none; width:32px; height:32px; border-radius:50%;
  display:inline-flex; align-items:center; justify-content:center;
  /* --acc-hi for the same reason as the tab count: 3.99:1 / 4.46:1 with --acc
     on this tint, 5.2:1 / 5.28:1 now (2026-10-02). */
  background:var(--acc-soft); color:var(--acc-hi); font-size:12px; font-weight:700;
}
/* Why the composer is disabled, under it (2026-10-02). */
.note-locked { margin:6px 0 0; }
.note-bubble {
  flex:1 1 auto; min-width:0; background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius);
}
.note-head {
  display:flex; align-items:baseline; gap:8px; padding:8px 14px;
  border-bottom:1px solid var(--line); background:var(--surface-2);
  border-radius:var(--radius) var(--radius) 0 0; font-size:13px;
}
.note-head b { color:var(--fg); font-weight:600; }
.note-head time { color:var(--mut); font-size:12px; }
.note-text { padding:10px 14px 12px; white-space:pre-wrap; overflow-wrap:anywhere; color:var(--fg-2); line-height:1.5; }
.note-compose-box {
  flex:1 1 auto; min-width:0; border:1px solid var(--line); border-radius:var(--radius);
  background:var(--surface); overflow:hidden;
}
.note-compose-box:focus-within { border-color:var(--acc); box-shadow:0 0 0 2px var(--acc-soft); }
.note-compose-box textarea {
  display:block; width:100%; min-height:84px; resize:vertical; border:0; outline:none;
  padding:10px 14px; font:inherit; color:var(--fg); background:transparent;
}
.note-compose-foot {
  display:flex; justify-content:flex-end; padding:8px 10px;
  border-top:1px solid var(--line); background:var(--surface-2);
}
.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;
}
.log-more > summary { cursor:pointer; color:var(--mut); font-size:12.75px;
  padding:8px 0; list-style:none; }
.log-more > summary::-webkit-details-marker { display:none; }
.log-more > summary:hover { color:var(--acc); }

@media (max-width:900px) {
  .detail { grid-template-columns:1fr; grid-template-areas:"head" "facts" "main"; }
}
@media (max-width:640px) {
  .detail h1 { font-size:23px; }
  .explainer-i dl { position:static; width:auto; margin-top:10px; }
}

/* ---- tables ------------------------------------------------------------- */

.table {
  width:100%; border-collapse:separate; border-spacing:0;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); overflow:hidden; font-size:14px;
  box-shadow:var(--shadow-sm);
}
.table th {
  text-align:left; font-size:var(--t-label); font-weight:700; text-transform:uppercase;
  letter-spacing:.085em; color:var(--fg-2); padding:11px 15px;
  background:var(--surface-2); border-bottom:1px solid var(--line-2);
  white-space:nowrap;
}
.table td { padding:13px 15px; border-bottom:1px solid var(--line); vertical-align:middle; }
.table tr:last-child td { border-bottom:0; }
.table tbody tr, .table tr { transition:background .12s; }
.table tr:hover td { background:var(--surface-3); }
.num { text-align:right; }
/* Beats `.table th`, which is more specific than a bare class. */
.table th.num { text-align:right; }

/* ---- activity log ------------------------------------------------------- */

.log { list-style:none; padding:0; margin:0; font-size:13.5px; }
.log li {
  padding:11px 0; border-bottom:1px solid var(--line);
  display:flex; gap:10px; flex-wrap:wrap; align-items:baseline; color:var(--fg-2);
}
.log li:last-child { border-bottom:0; }
.log .when { color:var(--mut); font-size:12.5px; flex:none; min-width:92px; }
.log b { color:var(--fg); font-weight:620; }
.log a { color:var(--acc); text-decoration:none; }
.log .log-what { display:flex; flex-direction:column; gap:2px; min-width:0; }
.log .log-subject { overflow-wrap:anywhere; }
.log a:hover { text-decoration:underline; text-underline-offset:2px; }

/* ---- stat row ----------------------------------------------------------- */

.stats { display:flex; gap:12px; flex-wrap:wrap; margin-bottom:20px; }
.stat {
  flex:1 1 158px; background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); padding:15px 17px; box-shadow:var(--shadow-sm);
}
.stat .v {
  font-size:var(--t-display); font-weight:var(--t-display-w);
  letter-spacing:var(--t-display-ls); line-height:1.1;
}
.stat .k { color:var(--mut); font-size:12.5px; margin-top:4px; line-height:1.45; }
/* An Outcomes number that opens the claims it counts. */
.stat a.v.stat-link { display:block; color:var(--acc); text-decoration:none; }
.stat a.v.stat-link:hover { text-decoration:underline; text-underline-offset:4px; }
/* My pipeline's Active / Decided headings carry their row count. */
.pipe-section .n { margin-inline-start:6px; font-size:.8em; font-weight:560; color:var(--mut); }
.pipe-filter b { color:var(--fg); margin-inline-end:4px; }

/* ---- section heading with an inline action ------------------------------ */

/* A heading and the control that acts on that section, on one line. The
   translate button belongs here rather than below the body: putting the control
   for reading a notice underneath the text you cannot read is backwards. */
.section-head {
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  margin:var(--s6) 0 10px;
}
.section-head:first-child { margin-top:0; }
.section-head :is(h3, .h-section), .rule-head :is(h2, h3) {
  margin:0; font-size:var(--t-section); letter-spacing:-.015em;
}

.icon-btn { display:inline-flex; align-items:center; gap:7px; }
.icon-btn svg { flex:none; }

/* Segmented control for switching between original and translation in place,
   so the two versions occupy the same spot instead of one sitting below the
   other and forcing a scroll past text you cannot read. */
.seg {
  display:inline-flex; border:1px solid var(--line-2); border-radius:999px;
  overflow:hidden; background:var(--surface);
}
.seg-btn {
  background:transparent; color:var(--fg-2); border:0; border-radius:0;
  padding:6px 15px; font:620 12.5px/1.3 inherit; cursor:pointer; box-shadow:none;
}
.seg-btn:hover { background:var(--surface-3); color:var(--fg); }
.seg-btn.on { background:var(--acc); color:var(--acc-fg); }
.seg-btn.on:hover { background:var(--acc-hi); }

/* ---- the organisation's matching rule, shown on /org -------------------- */
/* The rule is the product's core promise made inspectable, so it gets a card
   of its own rather than a row in a table. */
.rule-card {
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); padding:18px 20px; margin-top:22px;
  box-shadow:var(--shadow-sm);
}
.rule-head {
  display:flex; align-items:center; justify-content:space-between;
  gap:12px; flex-wrap:wrap; margin-bottom:15px;
}
.rule-body { display:flex; flex-direction:column; gap:10px; }
.rule-row { display:flex; gap:12px; align-items:baseline; flex-wrap:wrap; }
.rule-label {
  flex:none; min-width:212px; color:var(--fg-2);
  font-size:12.75px; font-weight:640;
}
/* Wraps rather than scrolls: a truncated rule is a rule you cannot check. */
.rule-value {
  display:inline-block; background:var(--surface-2);
  border:1px solid var(--line); border-radius:var(--radius-xs);
  padding:4px 9px; font-size:12.75px; line-height:1.7; color:var(--fg);
  word-break:break-word;
}
.rule-stats {
  display:flex; gap:24px; flex-wrap:wrap; align-items:baseline;
  margin-top:16px; padding-top:14px; border-top:1px solid var(--line);
  font-size:13px; color:var(--mut);
}
.rule-stats b {
  color:var(--fg); font-size:19px; font-weight:730;
  letter-spacing:-.03em; margin-right:4px;
}
.rule-more { margin-top:14px; }
.rule-more summary {
  cursor:pointer; color:var(--acc); font-size:13px; font-weight:560;
  width:fit-content; border-radius:var(--radius-xs);
}
.rule-more summary:hover { text-decoration:underline; text-underline-offset:3px; }
.rule-more p { margin:10px 0; }
.cpv-grid {
  display:grid; grid-template-columns:repeat(auto-fill, minmax(240px, 1fr));
  gap:7px 18px; margin-top:10px;
}
.cpv-grid code {
  background:var(--surface-2); border:1px solid var(--line);
  border-radius:var(--radius-xs); padding:1px 6px;
  font-size:12px; margin-right:6px;
}

/* Live match count under the profile form. Reserves its own line so the form
   does not jump every time the number changes. */
.live-count {
  display:flex; align-items:baseline; gap:7px; min-height:24px; flex-wrap:wrap;
  margin:2px 0 14px; font-size:13px; color:var(--mut);
}
.live-count b { color:var(--fg); font-size:17px; font-weight:730; letter-spacing:-.03em; }
.live-count.busy { opacity:1; }

/* ---- product tiles on /org ---------------------------------------------- */
/* One card per thing the organisation sells. A grid rather than a table: each
   tile carries a rule, three numbers and its own actions, which is more than a
   row can hold without becoming unreadable. */
.tiles {
  display:grid; grid-template-columns:repeat(auto-fill, minmax(min(330px, 100%), 1fr));
  gap:14px; margin-bottom:8px;
}
.tile {
  display:flex; flex-direction:column; gap:10px;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); padding:16px 18px; box-shadow:var(--shadow-sm);
  transition:border-color .14s, box-shadow .14s, transform .14s;
}
.tile:hover {
  border-color:var(--line-2); box-shadow:var(--shadow-hi); transform:translateY(-1px);
}
/* Retired products stay visible — their name is in the audit trail — but must
   not read as live. */
/* Retired products stay legible. They used to dim to opacity .62, which put
   their title at 5.00:1 while NORMAL secondary text sat at 4.49:1 — the app's
   disabled state was measurably stronger than its live text, and that
   inversion is exactly why the page read as switched off. Retirement is now
   signalled by a recessed ground and the "retired" pill, never by fading. */
.tile-off { background:var(--recessed); border-style:dashed; }
.tile-off .tile-name { color:var(--fg-2); }

/* There was no disabled styling anywhere in this codebase — a genuinely
   disabled control got pure user-agent default. Distinguish by TREATMENT
   (recessed, no elevation, not-allowed) rather than by being fainter than
   live text, or we recreate the inversion above. */
:disabled, [disabled], .is-disabled {
  cursor:not-allowed; box-shadow:none;
}
button:disabled, button[disabled], .btn:disabled, .chip.is-disabled {
  background:var(--recessed); color:var(--mut); border-color:var(--line-2);
}
input:disabled, select:disabled, textarea:disabled {
  background:var(--recessed); color:var(--mut); border-color:var(--line-2);
}
.tile-head { display:flex; align-items:center; gap:9px; }
.tile-name {
  font-size:15.5px; font-weight:660;
  letter-spacing:-.012em; text-decoration:none;
}
.tile-name:hover { color:var(--acc); text-decoration:underline; text-underline-offset:2.5px; }
.tile-sum { margin:0; }
.tile-nums {
  display:flex; gap:20px; flex-wrap:wrap; align-items:baseline;
  padding-top:11px; border-top:1px solid var(--line); margin-top:auto;
}
.tile-nums b {
  font-size:20px; font-weight:730; letter-spacing:-.03em; margin-right:4px;
  font-variant-numeric:tabular-nums;
}
.tile-nums span { color:var(--mut); font-size:12.5px; }
.tile-foot { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }

/* ---- logos, and the initials shown when there is none -------------------- */
/* A missing logo renders as text, not as a broken-image icon: no request is
   made at all, and the space where a brand belongs never looks broken. */
.mark {
  flex:none; display:inline-flex; align-items:center; justify-content:center;
  border-radius:var(--radius-sm); overflow:hidden;
  background:var(--surface-2); border:1px solid var(--line);
  font-weight:700; letter-spacing:-.02em; color:var(--mut);
  text-transform:uppercase; user-select:none;
}
.mark img { width:100%; height:100%; object-fit:contain; display:block; }
.mark-40 { width:40px; height:40px; font-size:14px; }
.mark-32 { width:32px; height:32px; font-size:12px; }
.mark-56 { width:56px; height:56px; font-size:18px; border-radius:var(--radius); }
/* Logos are usually drawn for white. On a dark surface, give them one so the
   dark half of a two-tone mark does not vanish into the card. */
:root:not([data-theme="light"]) .mark:has(img) { background:#fff; }
/* Logos are drawn for white; give them one in BOTH themes (this was dark-only)
   and an inset so a wide wordmark is not pressed against its own border. */
.mark:has(img) { background:#fff; padding:3px; }

/* The file input is the ugliest native control on the page; give it the same
   border language as everything else. */
input[type="file"] {
  padding:6px 8px; font-size:13px; background:var(--surface);
}
input[type="file"]::file-selector-button {
  margin-right:10px; padding:5px 12px; cursor:pointer;
  background:var(--surface-2); color:var(--fg-2);
  border:1px solid var(--line-2); border-radius:var(--radius-xs);
  font:600 12.5px/1.3 inherit;
}
input[type="file"]::file-selector-button:hover {
  border-color:var(--acc); color:var(--acc);
}
.logo-row { display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
.logo-row .grow { flex:1 1 240px; min-width:200px; }

/* An organisation's or product's own site, shown as its host. */
.site-link {
  display:inline-flex; align-items:center; gap:5px;
  color:var(--acc); text-decoration:none; font-size:13px;
}
.site-link:hover { text-decoration:underline; text-underline-offset:2.5px; }

.prose {
  color:var(--fg-2); font-size:14px; line-height:1.65; white-space:pre-wrap;
  max-width:76ch; margin:0;
}


/* ---- learned avoidance groups ------------------------------------------- */

/* One card per rule, so "why is this hidden" and "the things it hid" cannot be
   read apart. Depth stays one step: the group lifts off the page, the rows
   inside it do not lift off the group. */
.avoid-group {
  border:1px solid var(--line); border-radius:var(--radius);
  background:var(--surface); margin-bottom:14px; overflow:hidden;
}
.avoid-head {
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  flex-wrap:wrap; padding:13px 16px; background:var(--surface-2);
  border-bottom:1px solid var(--line);
}
.avoid-head b { font-weight:640; }
.avoid-head .sub { margin-top:3px; }
.avoid-group .rows { margin:0; }


/* ---- search bar ---------------------------------------------------------- */

/* Above the results and full width, because search is the first thing people
   reach for and it was buried four groups down a scrolling sidebar. */
.searchbar {
  display:flex; align-items:center; gap:10px; margin:0 0 6px;
  background:var(--surface); border:1px solid var(--line-2);
  border-radius:var(--radius-pill); padding:6px 8px 6px 16px;
  box-shadow:var(--shadow-sm);
}
.searchbar:focus-within { border-color:var(--acc); box-shadow:0 0 0 3px var(--acc-ring); }
.searchbar .ic { color:var(--mut); flex:none; }
.searchbar input[type=search] {
  flex:1; border:0; background:transparent; box-shadow:none; padding:8px 0;
  font-size:14.5px; color:var(--fg); min-width:0;
}
.searchbar input[type=search]:focus { outline:none; box-shadow:none; }
.searchbar button { flex:none; }
.searchhint { color:var(--mut); font-size:12.5px; margin:0 0 14px 16px; }

/* ---- facet overflow ------------------------------------------------------ */

.frow-more { margin-top:2px; }
.frow-more summary {
  cursor:pointer; color:var(--mut); font-size:12.5px; padding:5px 10px;
  border-radius:var(--radius-sm); list-style:none;
}
.frow-more summary::-webkit-details-marker { display:none; }
.frow-more summary:hover { color:var(--acc); background:var(--acc-soft); }
.frow-more[open] summary { color:var(--fg-2); }


/* ---- profile menu -------------------------------------------------------- */

/* One control instead of four. See layout.html for what this replaced and why
   the header could not fit it. */
.pmenu { position:relative; flex:none; }
.pmenu > summary {
  display:inline-flex; align-items:center; gap:5px; cursor:pointer;
  padding:4px 8px 4px 4px; border-radius:var(--radius-pill);
  border:1px solid transparent; list-style:none; white-space:nowrap;
}
.pmenu > summary::-webkit-details-marker { display:none; }
.pmenu > summary:hover { border-color:var(--line-2); background:var(--surface-2); }
.pmenu[open] > summary { border-color:var(--line-2); background:var(--surface-2); }
.pmenu > summary .ic { width:14px; height:14px; color:var(--mut); }
.pmenu[open] > summary .ic { transform:rotate(180deg); }

.pmenu-avatar {
  display:inline-flex; align-items:center; justify-content:center;
  width:30px; height:30px; border-radius:50%; flex:none;
  background:var(--acc); color:var(--acc-fg);
  font-size:12px; font-weight:750; letter-spacing:.02em;
}
/* One person per row inside a menu — the reassign picker — reads as a list,
   not a set of headers, so the mark sits at list-item scale. */
.pmenu-avatar.sm { width:20px; height:20px; font-size:9px; }

/* A person in the picker is a name over an address, so two colleagues whose
   display names collide are still told apart. The item itself is a flex row
   (avatar | person), and this is the column inside it. */
.pmenu-person { display:flex; flex-direction:column; gap:1px; min-width:0; }
.pmenu-person-sub {
  color:var(--mut); font-size:11.5px; font-weight:440;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* Who has it now, ahead of who could have it instead: the same fact the
   Assignee field already states, repeated here because a picker with eight
   names in it is exactly the moment someone forgets who they are replacing. */
.pmenu-current {
  display:flex; align-items:center; gap:8px;
  padding:8px 12px 10px; margin-bottom:4px;
  border-bottom:1px solid var(--line);
  color:var(--mut); font-size:12.5px;
}

/* Absolute inside the summary's positioned parent, never fixed: .top-bar sets
   backdrop-filter, which establishes a containing block, so a fixed panel would
   be positioned against the bar rather than the viewport anyway.

   Background and border match .card exactly (surface + line, not surface-2 +
   line-2) for the reason the ground-color comment up top gives for cards: on
   a near-white page, #fff on #fafbfd is a step so small the border has to
   carry the whole job of saying "this is a panel" — the softer pair reads as
   a floating smudge with a shadow under it, not an edge, which is exactly
   how this one rendered until it was pointed out. */
.pmenu-panel {
  position:absolute; inset-inline-end:0; top:calc(100% + 8px); z-index:60;
  min-width:248px; padding:6px;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); box-shadow:var(--shadow-hi);
}
.pmenu-who {
  padding:10px 12px 12px; border-bottom:1px solid var(--line); margin-bottom:6px;
  display:flex; flex-direction:column; gap:3px; align-items:flex-start;
}
.pmenu-who b { font-size:14px; font-weight:640; }
.pmenu-who > span { color:var(--mut); font-size:12.5px; }
.pmenu-who .pb { margin-top:4px; }

.pmenu-panel a, .pmenu-item {
  display:flex; align-items:center; gap:10px; width:100%;
  padding:9px 12px; border-radius:var(--radius-sm);
  color:var(--fg-2); text-decoration:none; font-size:13.5px; font-weight:520;
  background:transparent; border:0; box-shadow:none; cursor:pointer;
  text-align:left; font-family:inherit;
}
.pmenu-panel a:hover, .pmenu-item:hover { background:var(--surface-3); color:var(--fg); }
.pmenu-panel form { margin:0; }
.pmenu-panel .ic { color:var(--mut); flex:none; }

/* The utility row below the rule: reachable, but visibly quieter than
   Organisation / Platform / Account above it, so the panel reads as "go here"
   first and "or do one of these" second. */
.pmenu-panel a.pmenu-sub, .pmenu-item.pmenu-sub {
  padding:7px 12px; font-size:12.5px; font-weight:480; color:var(--mut);
}
.pmenu-panel a.pmenu-sub:hover, .pmenu-item.pmenu-sub:hover { color:var(--fg-2); }
.pmenu-panel a.pmenu-sub .ic, .pmenu-item.pmenu-sub .ic { width:13px; height:13px; }


/* ---- feed presets -------------------------------------------------------- */

.presets { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:16px; }
.presets .lbl { margin:0 4px 0 0; }
.presets .chip { cursor:pointer; font-family:inherit; }


/* ---- active filter chips ------------------------------------------------- */

.activefilters {
  display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin:0 0 16px;
}
.activefilters .lbl { margin:0 2px 0 0; }
/* Accent-tinted, unlike the neutral chips above: these are STATE, not links to
   somewhere else, and they should read as "this is on" at a glance. */
.chip-x {
  background:var(--acc-soft); border-color:color-mix(in srgb, var(--acc) 32%, transparent);
  color:var(--acc); font-weight:560; padding-right:8px;
}
.chip-x .x { font-size:15px; line-height:1; opacity:.75; margin-left:2px; }
.chip-x:hover { background:var(--acc); color:var(--acc-fg); border-color:var(--acc); }
.chip-x:hover .x { opacity:1; }
.chip-clear { color:var(--mut); }


/* ---- reply drafts -------------------------------------------------------- */

.draft {
  border:1px solid var(--line); border-radius:var(--radius);
  background:var(--surface); padding:14px 16px; margin-bottom:14px;
}
.draft-head {
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  flex-wrap:wrap; margin-bottom:10px;
}
.draft-head .sub { margin-left:6px; }


/* ---- what the buttons do ------------------------------------------------- */

/* Collapsed by default: reference, not a lecture on every visit. The trigger
   is the round ⓘ, not a chip-sized sentence. */
.explainer { margin:0 0 16px; }
.explainer > summary { list-style:none; width:max-content; }
.explainer > summary::-webkit-details-marker { display:none; }
.explainer[open] > summary.info-btn {
  color:var(--acc); border-color:var(--acc); background:var(--acc-soft);
}
.explainer dl {
  margin:12px 0 0; padding:14px 16px; display:grid; gap:6px 16px;
  grid-template-columns:max-content 1fr;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius);
}
.explainer dt { font-weight:640; color:var(--fg); white-space:nowrap; }
.explainer dd { margin:0; color:var(--fg-2); font-size:13.5px; line-height:1.55; }
.explainer a { color:var(--acc); }
@media (max-width:640px) { .explainer dl { grid-template-columns:1fr; } }

/* ---- detail page, issue-shaped additions --------------------------------
   The stage pill, the timeline, and the rail's action forms. Everything else
   the redesigned detail.html uses is the existing vocabulary. */
.state-pill { display:inline-flex; align-items:center; padding:4px 12px;
  border-radius:var(--radius-pill); font-size:var(--t-sub); font-weight:650;
  letter-spacing:.01em; }
.state-ok  { background:var(--ok); color:var(--okfg); }
.state-bad { background:var(--bad); color:var(--badfg); }
.state-acc { background:var(--acc-soft); color:var(--acc); }
.state-mut { background:var(--neutral); color:var(--neutralfg); }
.detail-ref { color:var(--mut); font-variant-numeric:tabular-nums;
  font-size:var(--t-sub); }

/* The timeline: a left rail with a dot per event, note events as bubbles. */
/* Logical sides throughout, so Arabic gets the rail on the right with the
   same 17px between a dot and its text: the RTL flip used to move the line
   and the dots but not the list's padding, so the dots fell outside the
   column and the text ran over the line (UI review 2026-09-26). */
.tml { list-style:none; margin:0 0 var(--s4); padding:0; padding-inline-start:18px;
  position:relative; }
.tml::before { content:""; position:absolute; inset-inline-start:5px; top:6px; bottom:6px;
  width:2px; background:var(--line); }
.tml li { position:relative; padding-block:5px; padding-inline:var(--s3) 0;
  display:flex; align-items:baseline; gap:var(--s2); font-size:14px; }
.tml li::before { content:""; position:absolute; inset-inline-start:-17px; top:11px;
  width:8px; height:8px; border-radius:50%; background:var(--surface-3);
  border:2px solid var(--line-2); }
.tml li.tml-note::before { background:var(--acc); border-color:var(--acc); }
.tml .tml-body { min-width:0; }
.tml .tml-body b { font-weight:650; }
.tml .tml-when { margin-inline-start:auto; flex:none; color:var(--mut);
  font-size:12.5px; white-space:nowrap; }
.tml li.tml-note .tml-body { background:var(--surface); border:1px solid
  var(--line); border-radius:var(--radius-sm); padding:8px 12px; }

/* Rail forms: full-width controls, stacked — the card IS the action's home. */
.facts .rail-form { display:flex; flex-direction:column; gap:8px;
  margin-top:10px; }
.facts .rail-form button, .facts .rail-form select { width:100%; }
.facts .rail-form .pmenu summary { justify-content:center; width:100%;
  box-sizing:border-box; }
/* The three quiet decisions share a line, so the rail shows Claim as the one
   promoted action and Keep / Ignore / Not a lead as equals beneath it rather
   than a stack of four full-width buttons all shouting equally. They wrap on a
   narrow rail instead of squeezing. */
.facts .rail-acts { display:flex; flex-wrap:wrap; gap:6px; }
.facts .rail-acts form { flex:1 1 auto; min-width:0; }
.facts .rail-acts button { width:100%; font-size:12.5px; padding:7px 8px; }
/* Kept is a STATE, not an invitation: it reads as already-on, and pressing it
   again is the undo. */
.facts .rail-acts button.on {
  border-color:var(--acc); color:var(--acc); background:var(--acc-soft);
}

/* Each quiet action is its button; what it does is explained by the dialog
   that opens on the first press (layout.html, #explain-dialog). The "i"
   badges that sat on each button are gone (owner, 2026-09-22). */
.facts .rail-acts .act-item { display:flex; flex:1 1 auto; min-width:0; }
.facts .rail-acts .act-item form { flex:1 1 auto; min-width:0; }

/* Whose pipeline you are reading. A row of names, the current one marked, so
   a manager can move between queues without going back to the team board. */
.pipeline-who { display:flex; align-items:center; flex-wrap:wrap; gap:6px; }
.pipeline-who .sub { margin-inline-end:2px; }
.pipeline-who .chip.on {
  border-color:var(--acc); color:var(--acc); background:var(--acc-soft);
  font-weight:600;
}
/* Open claims per person, the same pill the nav counts use. */
.pipeline-who .chip .n { font-size:11.5px; color:var(--mut); background:var(--surface-3);
  border-radius:999px; padding:0 7px; margin-inline-start:4px; font-variant-numeric:tabular-nums; }
.pipeline-who .chip.on .n { color:var(--acc); background:var(--surface); }
.tagrow { display:flex; flex-wrap:wrap; gap:5px; margin:6px 0; }
.facts-more { border-top:1px solid var(--line); margin-top:4px; }
.facts-more > summary { cursor:pointer; color:var(--mut);
  font-size:var(--t-sub); padding:10px 0 6px; list-style:none; }
.facts-more > summary::before { content:"▸ "; }
.facts-more[open] > summary::before { content:"▾ "; }

/* ---- account: a settings list, read down the left, acted on down the right */
.acct { max-width:760px; margin-inline:auto; }
.acct-card { background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); box-shadow:var(--shadow-sm); overflow:hidden; }
.acct-row, .danger-zone .dz-row {
  display:flex; align-items:flex-start; gap:var(--s5);
  justify-content:space-between; padding:16px 18px;
}
.acct-row + .acct-row, .danger-zone .dz-row { border-top:1px solid var(--line); }
.acct-what { min-width:0; display:flex; flex-direction:column; gap:3px; }
.acct-what b { font-weight:640; font-size:14.5px; }
.acct-what span { color:var(--mut); font-size:var(--t-sub); line-height:1.5; }
/* Packed to the RIGHT: the email row gives this box a minimum width, and a
   short control left-packed inside it (the On/Off switch, Replay now, the
   language select) floated in the middle of the row instead of lining up with
   every other button on the page (owner, 2026-09-22). */
.acct-do { flex:none; display:flex; align-items:center; justify-content:flex-end; gap:8px; }
/* The one row whose control is a text field needs room; everything else is a
   button that should not stretch. */
.acct-inline { min-width:min(320px, 46vw); }
.acct-inline input[type=email] { flex:1; min-width:0; }
/* The email change folds like the password one (.acct-fold/.acct-pw below):
   the address is read on the left, the form opens behind "Change email". */
.acct-what .acct-addr { color:var(--fg); overflow-wrap:anywhere; }

/* The digest switch: the label states the CURRENT state, and changing it
   submits — a preference with a Save button beside it is a preference people
   leave unsaved. */
.acct-switch { display:inline-flex; align-items:center; gap:8px; cursor:pointer;
  font-size:var(--t-sub); color:var(--fg-2); }
/* A real toggle, not a tick box (owner, 2026-09-22). Still a native checkbox
   underneath: the form, the keyboard (Space) and screen readers all keep
   working, and the label beside it still states On or Off. */
.acct-switch input[type=checkbox], input[type=checkbox].switch {
  appearance:none; -webkit-appearance:none; flex:none; margin:0; cursor:pointer;
  position:relative; width:38px; height:22px; border-radius:999px;
  background:var(--line-2); border:0; transition:background .15s ease; }
.acct-switch input[type=checkbox]::before, input[type=checkbox].switch::before {
  content:""; position:absolute; top:3px; inset-inline-start:3px; width:16px; height:16px;
  border-radius:50%; background:#fff; box-shadow:0 1px 2px rgba(0,0,0,.25);
  transition:transform .15s ease; }
.acct-switch input[type=checkbox]:checked, input[type=checkbox].switch:checked { background:var(--acc); }
.acct-switch input[type=checkbox]:checked::before, input[type=checkbox].switch:checked::before { transform:translateX(16px); }
[dir="rtl"] .acct-switch input[type=checkbox]:checked::before,
[dir="rtl"] input[type=checkbox].switch:checked::before { transform:translateX(-16px); }
.acct-switch input[type=checkbox]:disabled, input[type=checkbox].switch:disabled { opacity:.45; cursor:not-allowed; }
.acct-switch input[type=checkbox]:focus-visible, input[type=checkbox].switch:focus-visible { outline:2px solid var(--acc); outline-offset:2px; }
@media (prefers-reduced-motion:reduce) {
  .acct-switch input[type=checkbox], input[type=checkbox].switch, .acct-switch input[type=checkbox]::before, input[type=checkbox].switch::before { transition:none; } }

/* Folds: the rarely-used forms (change password, delete) stay closed so the
   page is five readable lines until you need one. */
.acct-fold > summary { list-style:none; cursor:pointer; }
.acct-fold > summary::-webkit-details-marker { display:none; }
.acct-pw { display:grid; gap:6px; margin-top:12px; min-width:min(320px, 60vw); }
.acct-pw label { margin-top:6px; }
.acct-pw button { margin-top:10px; justify-self:start; }
.chip.danger-chip { color:var(--alert); border-color:
  color-mix(in srgb, var(--alert) 45%, transparent); }

.danger-zone { border:1px solid color-mix(in srgb, var(--alert) 45%, transparent);
  border-radius:var(--radius); margin-top:var(--s5); overflow:hidden; }
.danger-zone .dz-head { color:var(--alert); font-weight:650;
  font-size:var(--t-sub); letter-spacing:.02em; padding:12px 18px 10px; }

@media (max-width:620px) {
  .acct-row, .danger-zone .dz-row { flex-direction:column; gap:10px; }
  .acct-do, .acct-inline { width:100%; }
  /* Stacked under its text on a phone, a control reads from the left. */
  .acct-do { justify-content:flex-start; }
}

/* ---- the notice text and the controls that act on it, as one card -------- */
.notice-card { background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); box-shadow:var(--shadow-sm); overflow:hidden; }
.notice-tools { display:flex; align-items:center; justify-content:flex-end;
  gap:10px; flex-wrap:wrap; padding:10px 14px;
  border-bottom:1px solid var(--line); background:var(--surface-2); }
.notice-tools select { padding:7px 10px; font-size:13.5px; border-radius:var(--radius-pill); }
/* Two different controls live on this bar and they were reading as one run of
   buttons: WHICH VERSION you are reading (the segmented pair) and MAKE A NEW
   TRANSLATION (label, target, button). Everything was flush right with a flat
   10px gap, so "Translate to" sat hard against its own select and the whole
   group crowded the version toggle. The toggle takes the free space on the
   start side, which pushes the translate control to the end and leaves a real
   gap between two things that do different jobs. */
.notice-tools .seg { margin-inline-end:auto; }
.notice-tools form { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.notice-tools form .sub { color:var(--mut); font-size:13px; white-space:nowrap; }
/* The body already carries the card chrome elsewhere; inside this card it is
   just the text. */
.detail .notice-card .body { background:transparent; border:0; box-shadow:none;
  border-radius:0; }
.detail .notice-card .hint { margin:14px 14px 0; }

/* The glossary opens IN FLOW — see the fragment comment. */
.explainer-inline { position:relative; }
.explainer-inline dl { width:min(560px, 100%); }

/* The listing's scope line: one sentence, quiet separators, links that read
   as links rather than as another filter chip. */
.scope-line { display:flex; align-items:baseline; gap:7px; flex-wrap:wrap; }
.scope-line a { color:var(--acc); text-decoration:none; }
.scope-line a:hover { text-decoration:underline; }
/* The set-aside count is a quiet fact, not an alarm: brand red beside
   "16 matched" read as an error to a new customer. */
.scope-line a.aside { color:var(--fg-2); }

/* ---- the website->AI assist: milestones, sliding bar, cancel ------------ */
.enrich { margin:10px 0; padding:12px 14px; background:var(--surface-2);
  border:1px solid var(--line); border-radius:var(--radius); }
.enrich-steps { display:flex; gap:14px; flex-wrap:wrap;
  font-size:var(--t-sub); color:var(--mut); }
.enrich-step.now { color:var(--fg); font-weight:640; }
.enrich-step.done { color:var(--okfg); }
.enrich-step.done::before { content:"✓ "; }
/* "Step 3 of 6", between the chips and the bar. It wore .sub alone, which is
   a PARAGRAPH style: muted and small, yes, but also a bottom margin meant to
   separate paragraphs, pushing the bar away from the counter it belongs to.
   Tabular figures because this line counts up in place, and proportional
   digits make it twitch sideways as it does. */
.enrich-progress { margin:8px 0 0; color:var(--mut); font-size:var(--t-sub);
  font-variant-numeric:tabular-nums; }
.enrich-bar { height:3px; margin-top:10px; border-radius:2px;
  background:var(--line); overflow:hidden; }
.enrich-bar i { display:block; height:100%; width:35%; background:var(--acc);
  animation:enrich-slide 1.2s ease-in-out infinite; }
@keyframes enrich-slide { from { margin-left:-35%; } to { margin-left:100%; } }
.enrich-cancel { margin-top:10px; }
.enrich-go { margin-top:6px; }
/* "Fill from website" shares one row with the Website field (fragments/enrich
   wraps both), so the field's help text below reads as describing the field,
   not the button. The field takes the room; the button wraps under it on a
   narrow screen. */
.enrich-row { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.dform .enrich-row > input, .enrich-row > input { flex:1 1 240px; width:auto; min-width:0; }
.enrich-row > .enrich-go { margin-top:0; flex:none; }
/* The feed tables (fragments/feedtable). The row actions STACK: three buttons side by
   side made the table wider than the 960px settings column, and Remove ended up out
   of view behind a scroll area with no visible scrollbar. */
.feed-acts { width:1%; min-width:270px; vertical-align:top; }
/* On a phone the feed row is a stacked block: five columns and three buttons
   do not fit 400px, and a scrolling table hid the buttons off screen. */
@media (max-width:600px) {
  .feed-table, .feed-table tbody, .feed-table tr, .feed-table td { display:block; width:auto; }
  .feed-table tr:first-child { display:none; }
  .feed-table tr { padding:10px 0; }
  .feed-table td { border-bottom:0; padding:3px 14px; max-width:none !important;
    white-space:normal !important; overflow-wrap:anywhere; }
  .feed-table td.num { text-align:left; }
  .feed-table .feed-acts { min-width:0; padding-top:8px; }
}
/* Small buttons that wrap (Check now and Turn off on one line, Remove under them),
   not a stack of three full-size ones: that made every row about 140px tall. */
.feed-acts-row { display:flex; flex-wrap:wrap; gap:6px; }
.feed-acts form { margin:0; }
.feed-acts button { padding:5px 10px; font-size:12.5px; line-height:1.3; white-space:nowrap; }
.feed-note { font-size:12px; line-height:1.45; margin:4px 0 0; max-width:320px; }
/* The tender page's earlier feeds (org-sources.html, #feed-others): folded under the
   last feed's table, one compact row each with its state and Turn off / Remove. */
.feed-others { margin-top:14px; border:1px solid var(--line); border-radius:10px;
  background:var(--surface); padding:0 14px; }
.feed-others > summary { cursor:pointer; padding:10px 0; font-weight:600;
  display:flex; flex-wrap:wrap; align-items:center; gap:8px; }
.feed-others > .sub { margin:0 0 8px; }
.feed-others-list { list-style:none; margin:0; padding:0; }
.feed-others-list > li { display:flex; flex-wrap:wrap; justify-content:space-between;
  align-items:flex-start; gap:8px 16px; padding:10px 0; border-top:1px solid var(--line); }
.feed-others-name { min-width:0; flex:1 1 260px; overflow-wrap:anywhere; }
.feed-others-name .pb { margin-inline-start:6px; }
.feed-others .feed-note { max-width:none; }
.feed-others form { margin:0; }
.feed-others .feed-acts-row button { padding:5px 10px; font-size:12.5px; line-height:1.3;
  white-space:nowrap; }
.enrich-note { margin-top:6px; }
.filled { animation:enrich-filled 1.6s ease-out 1; }
@keyframes enrich-filled { 0% { background:var(--ok); } 100% { background:transparent; } }
@media (prefers-reduced-motion: reduce) {
  .enrich-bar i, .filled { animation:none; }
}

/* ---- RTL (right-to-left) support ----------------------------------------
   The <html> element carries dir="rtl" for Arabic/Hebrew/Persian/Urdu. Rather
   than rewriting every directional rule as a logical property (a broad,
   regression-prone change), this block flips the handful of physical
   directions the layout actually depends on. Flex/grid already reorder
   automatically; only explicit left/right and text-align need mirroring. */
[dir="rtl"] .top-bar .head { direction: rtl; }
[dir="rtl"] .nav, [dir="rtl"] .lnav-links { direction: rtl; }
[dir="rtl"] .row-badges { margin-left:0; margin-right:2px; }
[dir="rtl"] .pcount { margin-left:0; margin-right:10px; }
[dir="rtl"] .hint, [dir="rtl"] .signin-alert, [dir="rtl"] .draft-note {
  border-left:none; border-right:3px solid var(--acc); }
[dir="rtl"] .hint.err { border-right-color:var(--alert); }
[dir="rtl"] .explainer-i dl { right:auto; left:0; }
[dir="rtl"] .fact-hero dd b { margin-right:0; margin-left:6px; }
[dir="rtl"] .info-tip { margin-left:0; margin-right:4px; }
[dir="rtl"] .table th, [dir="rtl"] .table td { text-align:right; }
[dir="rtl"] .num, [dir="rtl"] .table th.num, [dir="rtl"] .table td.num { text-align:left; }
[dir="rtl"] .chip-x .x { margin-left:0; margin-right:2px; }
[dir="rtl"] .draft-head .sub { margin-left:0; margin-right:6px; }
[dir="rtl"] .legal-wrap ul { padding-left:0; padding-right:20px; }
[dir="rtl"] .legal-table th, [dir="rtl"] .legal-table td { text-align:right; }
[dir="rtl"] .signin-links { direction: rtl; }
/* Directional chevrons point the way the page reads: in Arabic, Back and
   Previous point right, Next points left. They pointed left and right as in
   English (found live 2026-09-25), so Back pointed INTO the page. */
[dir="rtl"] svg.ic:has(> use[href="#i-left"]),
[dir="rtl"] svg.ic:has(> use[href="#i-right"]) { transform:scaleX(-1); }
/* What a customer typed (the organisation's About, a product's name and
   summary) is usually English: laid out right-to-left it put the full stop
   and the ellipsis at the wrong end (".accepting design partners"). Each
   paragraph takes its direction from its own text. */
[dir="rtl"] .org-about, [dir="rtl"] .tile-sum, [dir="rtl"] .tile-name { unicode-bidi:plaintext; }

/* The language menu holds 22 entries — without a ceiling it runs off the
   bottom of the viewport and the last languages are unreachable. Two columns
   keep it a glance rather than a scroll on a normal screen. */
/* A GRID, not CSS columns: a multicol box under a height cap sends what does
   not fit into extra columns SIDEWAYS and never scrolls down, so on a 390px
   phone only 11 of the 22 languages were inside the panel (scrollWidth 388 in
   198) and at 1280x600 two were lost off a third column. A grid overflows
   downwards, and overflow-y:auto scrolls it. Row order (English | Français…)
   is fine: the list is not alphabetical. */
.langmenu .pmenu-panel {
  max-height:min(70vh, 520px); overflow-y:auto;
  display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); column-gap:4px;
  align-content:start; min-width:320px;
}
/* On a phone the panel may be no wider than the room left of the language
   button (it ends at 100vw-89px on app pages), or it hangs off the start. */
@media (max-width:520px) {
  .langmenu .pmenu-panel { min-width:0; width:min(280px, calc(100vw - 100px)); }
}

/* ---- detail: previous / next ------------------------------------------- */
/* The detail page is where a person spends most of their time, and the only
   way out of it used to be Back to the list plus a re-scroll. */
.detail-nav { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  margin-bottom:6px; }
.detail-nav .dn-spacer { flex:1; }
.detail-nav .dn-btn { display:inline-flex; align-items:center; gap:6px; }
@media (max-width:560px) { .detail-nav .dn-spacer { display:none; } }

/* Round-four UI review fixes */
/* Reading pages (answers assembly): 200-char lines are unreadable at 1440. */
main.narrow { max-width:920px; margin-inline:auto; }
/* The copy-out textarea: 'wide' was referenced but never defined, so the
   drafted response rendered in a ~190px default box. */
.wide { width:100%; box-sizing:border-box; resize:vertical; }
/* 11 nav items overflow at ~1440px and the last one clips mid-word; tighten
   until they fit rather than relying on an invisible horizontal scroll. */
@media (max-width:1520px) {
  .nav a { padding:7px 8px; font-size:12.5px; gap:4px; }
  .nav a svg { width:14px; height:14px; }
}

/* --- Round-five facet rail polish -------------------------------------- */
/* The date facet: one visual unit, not four stacked form rows. The native
   picker follows :root color-scheme, so it opens dark in dark. */
.dform .date-scope {
  width:100%; background:var(--surface-3); border:1px solid var(--line-2);
  border-radius:8px; padding:8px 10px; font-size:13.5px; color:var(--fg);
}
/* min-width:0 is the whole fix for the cut-off "To" field, and it is not
   cosmetic. A grid item defaults to min-width:auto, which refuses to shrink
   below its CONTENT's minimum. An <input type=date> reports a min-content width
   of roughly 140px in Chrome (the dd/mm/yyyy mask plus the picker button), so
   two of them in a 1fr 1fr grid inside a ~250px sidebar column demanded ~280px,
   overflowed, and clipped the right-hand one. width:100% cannot rescue it
   because the floor is the minimum, not the preferred, width. */
.date-range { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:8px; }
.date-range .dr { min-width:0; }
.date-range .dr span {
  display:block; font-size:var(--t-label); font-weight:700; letter-spacing:.085em;
  text-transform:uppercase; color:var(--mut); margin:0 0 4px 2px;
}
.date-range input[type=date] {
  width:100%; min-width:0; box-sizing:border-box;
  background:var(--surface-3); border:1px solid var(--line-2);
  border-radius:8px; padding:7px 6px; font-size:12.5px; color:var(--fg);
  font-variant-numeric:tabular-nums;
}
/* The picker button is what pushes an already-tight field over. Shrinking the
   glyph buys back roughly 6px per input without hiding the affordance. */
.date-range input[type=date]::-webkit-calendar-picker-indicator {
  margin-inline-start:0; padding:0; width:14px;
}
.date-range input[type=date]:focus, .dform .date-scope:focus {
  outline:none; border-color:var(--acc); box-shadow:0 0 0 2px var(--acc-soft);
}
input[type=date]::-webkit-calendar-picker-indicator { opacity:.65; cursor:pointer; }
input[type=date]::-webkit-calendar-picker-indicator:hover { opacity:1; }
.date-actions { display:flex; gap:8px; margin-top:10px; }
.date-actions button { flex:1; }
/* Facet sections: a touch more presence — clearer separation, counts that
   read as data. Prominence from rhythm and type, not color (the sidebar must
   stay quieter than the list it filters). */
.frow .n { color:var(--mut); }
.frow:hover .n { color:var(--fg-2); }
.frow-more summary {
  cursor:pointer; font-size:12.5px; color:var(--mut); padding:5px 10px;
}
.frow-more summary:hover { color:var(--fg); }

/* --- Round six: the minimal organisation page ------------------------- */
/* One identity band replaces the old page-head + bordered facts card. */
.org-id { display:flex; align-items:center; gap:15px; margin:0 0 10px; flex-wrap:wrap; }
.org-id-text { min-width:0; }
/* The org name was an UNSTYLED h2 rendering at the browser's 1.5em (22.5px),
   so this page had no title at the top of the type scale while every other
   page's .page-head h2 is 28px. */
.org-id-text h2 {
  margin:0; line-height:1.18; font-size:var(--t-display);
  font-weight:var(--t-display-w); letter-spacing:var(--t-display-ls);
}
.org-id-meta { display:flex; flex-wrap:wrap; align-items:center; gap:4px 12px; margin-top:6px; }
/* The dots between facts are drawn, not typed — no separator spans in the
   markup, and none dangles when a fact is absent for a solo workspace. */
.org-id-meta > * + *::before { content:"·"; margin-inline-end:12px; color:var(--mut); }
.org-id-actions { margin-inline-start:auto; display:flex; gap:8px; flex-wrap:wrap; }
.org-about { margin:0; max-width:70ch; }
/* Phone fold (fragments/fold.html): about three lines and More, at 620px
   and below only, and only once the script has armed it (.fold-on). */
.fold-more { display:none; }
@media (max-width:620px) {
  .fold-sm.fold-on:not(.open) { max-height:4.8em; overflow:hidden; }
  .fold-more:not([hidden]) { display:inline-block; margin:4px 0 0; padding:2px 0; min-height:0;
    background:none; border:0; box-shadow:none; color:var(--acc); font-weight:600; cursor:pointer; }
}

/* Tiles: identical height by construction. Rows can no longer differ, and
   nothing inside a tile is unbounded now that the rule dump is gone. */
.tiles { grid-auto-rows:1fr; }
.tile-sum { min-height:3.2em; display:-webkit-box; -webkit-line-clamp:2;
            -webkit-box-orient:vertical; overflow:hidden; }
.tile-nums { gap:28px; }
.tile-foot { align-items:baseline; }

/* The kebab: invisible until wanted. --mut at rest, full weight on hover or
   while its panel is open, so a grid of tiles stays quiet. */
.tile-menu > summary { border:0; background:none; padding:5px; color:var(--mut);
                       border-radius:var(--radius-xs); }
.tile-menu > summary:hover, .tile-menu[open] > summary { color:var(--fg); background:var(--surface-3); }
.tile-menu > summary .ic { width:16px; height:16px; }
/* The chevron flip on .pmenu summary must not apply to a dots glyph. */
.tile-menu[open] > summary .ic { transform:none; }
.tile-menu .pmenu-panel { min-width:212px; }
.pmenu-rule { height:1px; background:var(--line); margin:5px 6px; }
.pmenu-danger { color:var(--alert); }
.pmenu-danger:hover { background:color-mix(in srgb, var(--alert) 14%, transparent); color:var(--alert); }
/* .note had NO RULE IN THIS STYLESHEET — not once in the repo's history —
   while eight templates used it. Every one of them rendered naked text on the
   page background where a card was intended. It is a component now. */
.note {
  display:flex; flex-direction:column; gap:6px; align-items:flex-start;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); padding:14px 16px; margin:16px 0;
  box-shadow:var(--shadow-sm);
}
/* Hierarchy by SIZE and COLOUR, not weight alone: the title and the body used
   to be the same 15px, 4px apart, differing only in font-weight. */
.note > b { font-size:var(--t-row); font-weight:640; color:var(--fg); }
.note > span, .note > p { font-size:var(--t-sub); color:var(--fg-2); margin:0; }
/* A note whose body folds (Renewals' "What this list knows"): the summary is
   the note's title, each body span its own line, as in a plain .note. */
.note-knows { width:100%; }
.note-knows > summary { font-size:var(--t-row); font-weight:640; color:var(--fg); cursor:pointer; }
.note-knows > span { display:block; font-size:var(--t-sub); color:var(--fg-2); margin:6px 0 0; }
/* A link that leaves for another site in a new tab. */
.ext-link .ext-mark { font-size:.85em; opacity:.75; }
.note-warn {
  background:color-mix(in srgb, var(--warn) 55%, var(--surface));
  border-color:color-mix(in srgb, var(--warnfg) 24%, var(--line));
  box-shadow:none;
}
.note-warn > b { color:var(--warnfg); }

/* Round eight: the nudge's actions, and the proposal review screen. */
.note-acts { display:flex; gap:8px; flex-wrap:wrap; margin-top:4px; }
.two-col { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
@media (max-width:720px) { .two-col { grid-template-columns:1fr; } }
.atext { font-size:var(--t-sub); color:var(--fg); }
.atext.quote {
  border-left:2px solid var(--line-2); padding-left:10px; color:var(--fg-2);
  font-style:italic;
}

/* ---- answer library ----------------------------------------------------- */
/* The topic cards render one <b>question</b>, its answer and an action row per
   answer, and had NO dedicated styling — so two answers in a topic ran together
   as one undivided block (measured on /answers: the "Other" card showed a
   cyber-insurance answer and an ISO-27001 answer with nothing between them).
   Scoped under .answers so the generic .card everywhere else is untouched. */
.answers-lead {
  max-width:74ch; margin:2px 0 18px; color:var(--fg-2);
  font-size:var(--t-sub); line-height:1.65;
}
.answers-lead b { color:var(--fg); font-weight:620; }
/* Inline badges in the lead sit in running text, so they may not pick up the
   flex baseline the row versions get — keep them on the text line. */
.answers-lead .pb { vertical-align:baseline; margin:0 1px; }

.answers .card { padding:4px 20px 10px; margin:0 0 16px; }
.answers .card > h3 {
  font-size:11.5px; font-weight:700; letter-spacing:.07em; text-transform:uppercase;
  color:var(--mut); margin:0; padding:15px 0 2px;
}
/* Each answer is a row with a hairline above it; the first one under the topic
   heading needs no rule. */
.answers .arow { padding:15px 0; border-top:1px solid var(--line); }
.answers .card > h3 + .arow { border-top:0; padding-top:8px; }
.answers .aq { display:flex; flex-wrap:wrap; align-items:center; gap:6px 9px; }
.answers .aq > b {
  font-size:15px; font-weight:640; color:var(--fg); letter-spacing:-.01em;
  line-height:1.35;
}
.answers .arow .atext {
  margin:7px 0 0; max-width:76ch; line-height:1.55; color:var(--fg-2);
}
.answers .ameta {
  display:flex; flex-wrap:wrap; align-items:center; gap:4px 16px; margin-top:11px;
  font-size:12.5px; color:var(--mut);
}
.answers .ameta > span { color:var(--mut); }         /* the evidence note */
.answers .ameta a {
  color:var(--mut); text-decoration:underline; text-underline-offset:2px;
}
.answers .ameta a:hover { color:var(--acc); }
.answers .ameta .inline { margin:0; }
.answers .ameta button.chip { padding:3px 11px; font-size:12px; }
@media (max-width:620px) { .answers .card { padding:4px 15px 8px; } }
/* The determinate crawl bar: a real fraction, not a sliding guess. */
.enrich-bar.determinate i { animation:none; margin-left:0; transition:width .3s ease; }
/* The write step. Its width is honest — one of two steps done — so it must not
   grow on a timer; instead it BREATHES, which says "working" without claiming
   any further progress. A bar that stands perfectly still for thirty seconds
   is what read as a hang. */
.enrich-bar.determinate.writing i { animation:enrich-breathe 1.6s ease-in-out infinite; }
@keyframes enrich-breathe { 0%,100% { opacity:1; } 50% { opacity:.45; } }
.enrich-doing { margin-top:8px; color:var(--fg-2); font-size:12.5px;
  font-variant-numeric:tabular-nums; line-height:1.45; }
@media (prefers-reduced-motion: reduce) {
  .enrich-bar.determinate.writing i { animation:none; }
}
.enrich-suggest {
  margin:6px 0 0; padding:9px 11px; background:var(--surface-2);
  border:1px dashed var(--line-2); border-radius:var(--radius-sm);
}
.enrich-suggest-text { font-size:var(--t-sub); color:var(--fg-2); white-space:pre-line; }
.enrich-suggest-acts { display:flex; gap:8px; margin-top:8px; }

/* "The AI is reading" on a product tile: a live state, not a zero. The dot
   pulses so a page left open reads as working rather than stalled. */
.tile-reading { display:flex; align-items:center; gap:8px; }
.tile-reading .dot { width:7px; height:7px; border-radius:50%;
  background:var(--acc); flex:none; animation:tile-pulse 1.4s ease-in-out infinite; }
@keyframes tile-pulse { 0%,100% { opacity:.35; } 50% { opacity:1; } }
@media (prefers-reduced-motion:reduce) {
  .tile-reading .dot { animation:none; opacity:.8; }
}

/* Search box inside a long facet group (country). Sized to the sidebar column
   and styled off the existing tokens so it inherits the theme, light and dark,
   without a second definition to keep in sync. */
.facet-find {
  width:100%; margin:4px 0 6px; padding:6px 8px;
  font:inherit; font-size:13px; color:var(--fg);
  background:var(--surface-2); border:1px solid var(--line); border-radius:7px;
}
.facet-find:focus { outline:2px solid var(--acc); outline-offset:1px; }

/* Save search: a disclosure, not a permanently-open form. The naming input and
   the digest checkbox used to sit in the filter bar next to the active-filter
   chips, where an empty text box reads as a stray field rather than part of a
   deliberate action. Styled from existing tokens so it tracks both themes. */
.savesearch { position:relative; display:inline-block; }
.savesearch > summary { list-style:none; cursor:pointer; }
.savesearch > summary::-webkit-details-marker { display:none; }
.savesearch-body {
  position:absolute; z-index:40; top:calc(100% + 6px); inset-inline-start:0;
  display:flex; flex-wrap:wrap; align-items:center; gap:8px;
  min-width:260px; padding:10px;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); box-shadow:var(--shadow-sm);
}
.savesearch-name {
  flex:1 1 150px; min-width:0; font:inherit; font-size:12.5px;
  padding:6px 9px; color:var(--fg);
  background:var(--surface-2); border:1px solid var(--line); border-radius:7px;
}
.savesearch-name:focus { outline:none; border-color:var(--acc); box-shadow:0 0 0 2px var(--acc-soft); }
.exportmenu > summary.btn-sec { display:inline-flex; align-items:center; gap:6px; }
.exportmenu-body {
  position:absolute; z-index:40; top:calc(100% + 6px); inset-inline-end:0;
  display:flex; flex-direction:column; min-width:250px; padding:6px;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); box-shadow:var(--shadow-sm);
}
.exportmenu-body a {
  display:flex; flex-direction:column; gap:1px; padding:8px 10px; border-radius:7px;
  color:var(--fg); text-decoration:none;
}
.exportmenu-body a:hover, .exportmenu-body a:focus-visible { background:var(--surface-2); outline:none; }
.exportmenu-body b { font-size:13px; font-weight:600; }
.exportmenu-body span { font-size:12px; color:var(--mut); }

/* Sort bar: sits directly above the result rows, reading as one line rather
   than as another filter group. Wraps on narrow viewports instead of forcing
   the results column to scroll sideways. */
.sortbar {
  display:flex; flex-wrap:wrap; align-items:center; gap:4px;
  margin:0 0 10px; padding:0 2px;
}
.sortbar-lbl {
  font-size:var(--t-label); font-weight:700; letter-spacing:.085em;
  text-transform:uppercase; color:var(--mut); margin-inline-end:6px;
}
.sortbar-opt, .sortbar-size {
  padding:4px 10px; border-radius:999px; font-size:13px;
  color:var(--mut); text-decoration:none; border:1px solid transparent;
  white-space:nowrap;
}
.sortbar-opt:hover, .sortbar-size:hover { background:var(--surface-3); color:var(--fg); }
/* Page size, at the far end of the same bar. */
.sortbar-right { margin-inline-start:auto; display:inline-flex; align-items:center; gap:4px; }
.sortbar-right .sortbar-size { padding:4px 8px; font-variant-numeric:tabular-nums; }
/* The active order is stated, not merely tinted: this control was invisible
   once already, so which order is in force should survive a glance. */
.sortbar-opt.on, .sortbar-size.on {
  background:var(--acc-soft); border-color:var(--acc);
  color:var(--acc); font-weight:650;
}

/* ---- what leaves today -------------------------------------------------- */
/* Retention DELETES expired tenders and posts past the window, so this band is
   a warning, not a statistic. Amber rather than the accent: the accent means
   "press me", and there is nothing to press here except the Keep on the rows
   below. It renders only when the count is non-zero — a permanent "0 leaving
   today" strip would train the eye to skip the one day it matters. */
.leaving {
  display:flex; align-items:center; gap:7px; flex-wrap:wrap;
  margin:8px 0 0; padding:7px 11px;
  border:1px solid var(--warn); border-radius:var(--radius-xs);
  background:var(--warn); color:var(--warnfg);
  font-size:12px; font-weight:600;
}
.leaving .ic { width:14px; height:14px; flex:none; opacity:.85; }
.leaving .sub { margin:0; font-weight:500; opacity:.85; }

/* The platform operator's read-only view banner. Warn tokens rather than the
   alert ones: it is a standing state to notice, not an error to fix. */
.hint.viewas {
  background:var(--warn); color:var(--warnfg); border:1px solid var(--alert);
  display:flex; flex-wrap:wrap; align-items:center; gap:8px;
}
.hint.viewas form { margin:0; }

/* ---- the row menu ------------------------------------------------------- */
/* A quiet 30px target that darkens with the row it belongs to; the panel is
   the header's own .pmenu-panel, right-aligned under the dots. Specific to
   .row-acts so the header menu's pill summary does not bleed in. */
.row-acts .rowmenu { position:relative; }
.row-acts .rowmenu > summary {
  display:inline-flex; align-items:center; justify-content:center;
  width:30px; height:30px; padding:0; border-radius:var(--radius-sm);
  border:1px solid transparent; background:transparent; color:var(--mut);
  cursor:pointer; list-style:none;
  transition:background .12s, color .12s, border-color .12s;
}
.row-acts .rowmenu > summary::-webkit-details-marker { display:none; }
.row-acts .rowmenu > summary .ic { width:16px; height:16px; color:inherit; }
.row:hover .row-acts .rowmenu > summary,
.row-acts .rowmenu > summary:hover,
.row-acts .rowmenu[open] > summary {
  color:var(--fg); background:var(--surface-3); border-color:var(--line-2);
}
.row-acts .rowmenu[open] > summary .ic { transform:none; }
.row-acts .rowmenu .pmenu-panel {
  inset-inline-end:0; inset-inline-start:auto; top:calc(100% + 4px);
  min-width:196px; padding:5px;
}
.row-acts .rowmenu form { margin:0; }
.row-acts .rowmenu .pmenu-item { width:100%; text-align:start; font-size:13px; padding:8px 11px; }
/* The one action most rows exist for leads the list, in the row's own ink. */
.row-acts .rowmenu .pmenu-item.primary { color:var(--fg); font-weight:640; }

/* ======================================================================
   v2 (2026-09-03): the component set the owner approved in the mockups.
   Overrides earlier rules by coming later at equal specificity, so every
   page inherits it at once; the rules above stay as the documented history
   of why each component is shaped the way it is.
   ====================================================================== */
.wrap { max-width:1440px; padding:24px 28px 56px; }
/* page header: title, summary, actions */
.page-head { margin-bottom:18px; }
.page-head h1, .page-head h2 { font-size:26px; font-weight:700; letter-spacing:-.025em; line-height:1.2; margin:0; }
.page-head .sub { margin:6px 0 0; font-size:13.5px; color:var(--mut); }
.page-head .sub b { color:var(--fg); font-weight:600; }
.page-head .eyebrow { font-size:11px; font-weight:700; letter-spacing:.09em; text-transform:uppercase; color:var(--mut); margin-bottom:6px; }
.page-actions { display:flex; gap:8px; align-items:center; margin-inline-start:auto; }
/* notices: one shape, a tone, an action at the end of the sentence.
   BLOCK, not flex. This rule said `display:flex` and no .hint on the site
   leads with an icon, so flex bought nothing and cost a great deal: a hint is
   usually a PARAGRAPH, and flex blockifies every child of it. `<b>Reddit:</b>`
   became its own column, the prose around it became more columns, and the tail
   of the sentence was pushed off the right edge and lost — measured on
   /org/sources at 390px, where the paragraph rendered as four narrow stacks and
   "Do that rather than adding one feed each…" was not on the page at all. 25 of
   the 67 hints carry inline <b>, <code> or <a>, so this was site-wide and at
   every width. The one hint that genuinely IS a row, .hint.viewas, declares its
   own flex below and keeps it. */
.hint {
  display:block; padding:11px 14px; margin:0 0 16px;
  border-radius:10px; border:1px solid color-mix(in srgb, var(--infofg) 22%, transparent);
  border-left-width:1px; background:var(--info); color:var(--infofg); font-size:13.5px; line-height:1.5;
}
.hint b { color:inherit; font-weight:650; }
.hint.err { border-color:color-mix(in srgb, var(--badfg) 28%, transparent); background:var(--bad); color:var(--badfg); }
/* A caution that is not an error: the Paddle sandbox (test-mode) notice on
   /pay and /org/billing. The --warn/--warnfg pair is set for both themes. */
.hint.warn { border-color:color-mix(in srgb, var(--warnfg) 28%, transparent); background:var(--warn); color:var(--warnfg); }
[dir="rtl"] .hint.warn { border-right-color:color-mix(in srgb, var(--warnfg) 28%, transparent); }

/* The "Ask a question" answers panel on /feedback.
   Borrows .hint's TONE but not its box, because .hint is the one-line notice
   above: display:flex, row. A heading plus four <details> dropped into it
   became six squished side-by-side columns, which is how this shipped.
   It declares NO display on purpose: a div is block already, and any author
   display here would beat the UA stylesheet's [hidden]{display:none} and leave
   the panel open for all four categories instead of just "Ask a question". */
.fhelp {
  padding:12px 15px; margin:12px 0 18px;
  border-radius:10px; border:1px solid color-mix(in srgb, var(--infofg) 22%, transparent);
  background:var(--info); color:var(--infofg); font-size:13.5px; line-height:1.55;
}
.fhelp > b { display:block; margin:0 0 2px; font-weight:650; }
.fhelp details { border-top:1px solid color-mix(in srgb, var(--infofg) 18%, transparent); }
.fhelp details:first-of-type { border-top:0; }
.fhelp summary { cursor:pointer; padding:7px 0; font-weight:600; }
.fhelp details p { margin:0 0 9px; }
.fhelp a { color:inherit; text-decoration:underline; }
.fhelp .sub { margin:9px 0 0; font-size:12.5px; color:inherit; opacity:.8; }
.leaving, .page-head .leaving {
  display:flex; align-items:center; gap:12px; padding:11px 14px; margin:14px 0 16px;
  border-radius:10px; border:1px solid color-mix(in srgb, var(--warnfg) 26%, transparent);
  background:var(--warn); color:var(--warnfg); font-size:13.5px; font-weight:normal;
}
.leaving .ic { width:16px; height:16px; flex:none; opacity:1; }
.leaving .leaving-msg { flex:1; min-width:0; }
.leaving .leaving-keep { opacity:.85; }
.leaving .act { margin-inline-start:auto; font-weight:600; text-decoration:underline; text-underline-offset:3px; color:inherit; white-space:nowrap; }
.leaving a { color:inherit; font-weight:650; }
/* Keep all: a second link-like action beside Show them, a real POST form. */
.leaving .leaving-keepall { margin:0; flex:none; }
.leaving .leaving-keepall .act {
  margin:0; padding:0; height:auto; min-height:0; border:0; background:none; box-shadow:none;
  font:inherit; font-weight:650; color:inherit; cursor:pointer;
  text-decoration:underline; text-underline-offset:3px; white-space:nowrap;
}
/* On a phone the two actions take their own line under the sentence. */
@media (max-width:560px) {
  .leaving { flex-wrap:wrap; }
  .leaving .leaving-msg { flex:1 1 calc(100% - 40px); }
  .leaving .act { margin-inline-start:28px; }
  .leaving .leaving-keepall .act { margin-inline-start:0; }
}
/* buttons: 8px corners, one height, weight 600 */
button, a.btn { border-radius:var(--radius-sm); padding:8px 14px; font-weight:600; font-size:13.5px; line-height:1.3; }
button.ghost { font-weight:560; }
a.chip, .chip { border-radius:var(--radius-sm); font-weight:560; }
.chip-x, .activefilters .chip, .presets .chip { border-radius:999px; }
/* sort and per-page as segmented controls */
.sortbar { gap:6px; margin:0 0 12px; }
.sortbar-opt, .sortbar-size { padding:6px 11px; border-radius:7px; font-weight:520; color:var(--mut); border:1px solid transparent; }
.sortbar-opt:hover, .sortbar-size:hover { background:var(--surface-3); color:var(--fg); }
.sortbar-opt.on, .sortbar-size.on { background:var(--surface); color:var(--fg); font-weight:600; border-color:var(--line-2); box-shadow:var(--shadow-sm); }
/* cards and tables */
.card { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm); padding:16px 20px; }
/* The list card is a card whose rows fill it edge to edge (mockup A): the
   per-row divider and hover run to the border, so the generic card padding
   above must NOT apply — the rows carry their own. */
.list-card { padding:0; }
.table th { font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--mut); font-weight:700; padding:9px 15px; background:var(--surface-2); }
.table td { padding:11px 15px; font-size:13.5px; }
/* the rail: full-bleed group dividers, clipped to the card radius (mockup A) */
.side { padding:0; overflow:hidden; }
details.fgroup > summary { padding:12px 14px; }
.fbody { padding:0 8px 10px; }
.frow { padding:6px 10px; border-radius:var(--radius-xs); }
details.fgroup[open] > summary .chev { transform:none; }
.frow:hover .n { color:var(--mut); }
/* rows */
.row-main { min-height:62px; }
.row-ttl { font-size:14.5px; }
.row-meta { font-size:12px; }
/* pager */
.pager a { min-width:36px; height:36px; display:inline-flex; align-items:center; justify-content:center; padding:0 10px; border-radius:var(--radius-sm); font-weight:560; }
.pager a.cur { box-shadow:none; }

/* --- v2 list page header + segmented toolbar (2026-09-03, matches mockup A) --- */
.page-head-row { display:flex; align-items:flex-end; gap:20px; }
.page-head-main { min-width:0; flex:1; }
.page-head .eyebrow { font-size:11px; font-weight:700; letter-spacing:.09em; text-transform:uppercase; color:var(--mut); margin-bottom:6px; }
.page-head h1, .page-head h2 { font-size:26px; font-weight:700; letter-spacing:-.025em; line-height:1.2; margin:0; }
.page-head .scope-line { margin-top:8px; font-size:13.5px; }
.page-head .scope-line b { color:var(--fg); font-weight:600; }
.page-actions { display:flex; align-items:center; gap:8px; flex:none; }
/* the two header buttons share one shape */
.page-actions .btn-sec, .page-actions .savesearch > summary {
  display:inline-flex; align-items:center; gap:7px; height:36px; padding:0 14px;
  border-radius:var(--radius-sm); border:1px solid var(--line-2); background:var(--surface);
  color:var(--fg-2); font-size:13.5px; font-weight:600; cursor:pointer; white-space:nowrap; list-style:none; text-decoration:none;
}
.page-actions .btn-sec svg, .page-actions .savesearch > summary svg { width:15px; height:15px; opacity:.8; }
.page-actions .btn-sec:hover, .page-actions .savesearch > summary:hover { border-color:var(--acc); color:var(--acc); background:var(--acc-soft); }
.page-actions .savesearch { position:relative; }
.page-actions .savesearch > summary::-webkit-details-marker { display:none; }
.page-actions .savesearch-body {
  /* left:auto is what makes right:0 mean anything. The base rule above sets
     inset-inline-start:0, and with BOTH a left offset and a width the browser
     ignores right entirely — so this panel anchored to the LEFT of a trigger
     that sits at the right edge of the page and ran off screen at every width.
     Measured with the popover OPEN: 1464 in a 1440 viewport, 792 in 768, and
     414 in 390, sliding the document each time. Every earlier check missed it
     because the popover was CLOSED. */
  /* Both offsets are logical, because mixing them is what broke this in RTL:
     inset-inline-start:auto resolves to right:auto in Arabic, and the physical
     right:0 after it then pinned the panel to the page's right while its
     trigger had moved to the left — measured open at x=-36 in a 1512 viewport
     and x=-72 in 390, sliding the document to 1548 and 462. Anchored to the
     inline END instead, the panel grows inward from the trigger in either
     direction. */
  position:absolute; inset-inline-start:auto; inset-inline-end:0;
  top:calc(100% + 6px);
  z-index:40; width:min(320px, 90vw);
  background:var(--surface); border:1px solid var(--line-2); border-radius:var(--radius);
  box-shadow:var(--shadow-hi); padding:14px; display:flex; flex-direction:column; gap:10px;
}
/* The base .savesearch-name rule carries flex:1 1 150px from the days this
   sat in a ROW. In this column that basis applies to HEIGHT, and the name box
   rendered ~200px tall (owner's screenshot, 2026-09-21). flex:none restores a
   normal one-line input. */
.page-actions .savesearch-name { width:100%; flex:none; height:36px; font-size:14px;
  padding:0 11px; box-sizing:border-box; }
.page-actions .savesearch-body { width:min(300px, 90vw); gap:10px; padding:14px 14px 12px;
  align-items:stretch; text-align:start; }
.savesearch-title { font-size:13.5px; font-weight:650; color:var(--fg); }
.savesearch-foot { display:flex; align-items:center; justify-content:space-between; gap:10px; }
.savesearch-digest { display:inline-flex; align-items:center; gap:6px; margin:0;
  font-size:13px; font-weight:500; color:var(--fg-2); cursor:pointer; }
.savesearch-digest input { width:auto; margin:0; }
.savesearch-save { height:32px; padding:0 16px; font-size:13.5px; }
@media (max-width:720px) {
  .page-head-row { flex-direction:column; gap:12px; }
  .page-actions { align-self:stretch; }
  .page-actions .savesearch, .page-actions .btn-sec { flex:1; justify-content:center; }
}

/* SORT + PER PAGE as one segmented control on a light track, not a labelled
   row of links (the mockup). The sort bar sits above the results column. */
.sortbar { display:flex; align-items:center; gap:10px; margin:0 0 12px; padding:0; }
.sortbar-lbl { font-size:11px; font-weight:700; letter-spacing:.09em; text-transform:uppercase; color:var(--mut); }
.sortbar .seg, .sortbar-seg { display:inline-flex; background:var(--surface-3); border-radius:var(--radius-sm); padding:3px; gap:2px; }
.sortbar-opt, .sortbar-size {
  padding:6px 11px; border-radius:6px; font-size:13px; font-weight:520; color:var(--mut);
  border:1px solid transparent; text-decoration:none; white-space:nowrap;
}
.sortbar-opt:hover, .sortbar-size:hover { background:var(--surface); color:var(--fg); }
.sortbar-opt.on, .sortbar-size.on { background:var(--surface); color:var(--fg); font-weight:600; box-shadow:var(--shadow-sm); }
.sortbar-right { margin-inline-start:auto; display:inline-flex; align-items:center; gap:8px; }
.sortbar-right .sortbar-lbl { margin:0; }

/* NARROW SCREENS. The four sort chips are one non-wrapping segmented control
   484px wide; in a 390px viewport that pushed the whole DOCUMENT sideways
   (measured: scrollWidth 512 against a 390 viewport, on the tenders lane and
   the set-aside lane alike). A page that slides under the thumb is worse than
   a control the thumb has to slide, so the toolbar wraps onto two lines and
   each segment scrolls WITHIN ITSELF rather than widening the page. */
@media (max-width:620px) {
  /* SUPERSEDED by the density pass: wrapping onto two lines fixed the sideways
     slide but cost 108px on a screen that was already 84% chrome. One nowrap
     row that scrolls as a whole does both — the page cannot slide, and the
     toolbar costs 44px instead of 108. */
  .sortbar { flex-wrap:nowrap; row-gap:0; overflow-x:auto; scrollbar-width:none; }
  .sortbar::-webkit-scrollbar { display:none; }
  .sortbar > .sortbar-lbl, .sortbar-right .sortbar-lbl { display:none; }
  .sortbar-right { flex:none; }
  .sortbar .seg, .sortbar-seg { flex:none; }
  .sortbar-right { margin-inline-start:0; }
  /* (The segments' own inner scroller from the two-line approach is gone: left
     in, it clamped the sort segment to the strip's width and made it a second
     scroller INSIDE the strip, so "Recently published" was cut hard at the
     edge with no fade and "Highest value" was reachable only by swiping inside
     the segment. The strip alone scrolls now; its end fade covers whatever is
     really cut.) */
  /* The save-search popover: ANCHOR IT TO THE RIGHT so it grows inward.
     The first attempt here only relaxed min-width, and that fixed nothing —
     measured with the popover OPEN at 390px, it sat at left:94 and 94+320 put
     its edge at 414, so the document still went to 414 and the page still slid.
     The width was never the problem; the left anchoring was. Verifying it with
     the popover CLOSED is what hid that from me. */
  /* On a phone, anchor the panel to the ACTIONS ROW rather than to the button.
     .savesearch is the positioned ancestor, so right:0 aligns to the button's
     own right edge — at 390px that edge is x=227, and a 320px panel from there
     runs to x=-93, hanging off the LEFT of the screen. Anchored to
     .page-actions instead, right:0 is the content edge and the panel fits.
     Measured after: 46..366 inside 390, nothing off either edge. */
  .page-actions { position:relative; }
  /* Specificity matters here: a bare .savesearch rule loses to the
     .page-actions .savesearch one further up, so the details stayed the
     positioned ancestor and right:0 kept aligning to the button. Matching that
     selector's weight is what actually releases the anchor. */
  .page-actions details.savesearch { position:static; }
  .savesearch-body { min-width:0; max-width:calc(100vw - 24px); }
}

/* The rows sit in a white card (mockup A): the list and the rail read as the
   two panels of one layout. Rounded, clipped, so the first and last row honour
   the corner; the per-row divider and hover are unchanged inside it. */
.list-card { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm); overflow:hidden; }
.list-card .rows { margin:0; }
.list-card .row:first-child { border-top:0; }
.list-card .empty, .list-card .hint { margin:0; border-radius:0; }
.list-card .hint { border-left:0; border-right:0; border-top:0; }
/* the empty-state, alone in the card, keeps its air */
.list-card > .empty:only-child { padding:52px 24px; }

/* The detail hero's at-a-glance chip cluster (mockup A). */
.detail-badges { display:flex; flex-wrap:wrap; gap:6px; margin:12px 0 0; }

/* ---- hidden means hidden ------------------------------------------------ */
/* `hidden` is a UA-stylesheet rule, and an author stylesheet beats the UA
   stylesheet at ANY specificity — so a class that declares `display` on a
   hidden element un-hides it. /feedback's help panel is written
   `<div id="feedback-help" class="hint" hidden>` with a comment saying the UA
   display:none wins; it does not, because .hint declares a display. The panel
   is meant to appear only for the category you pick, and instead it rendered
   on every visit. Measured at 390px on 2026-09-06: it was on the page, above
   Subject, in six squished columns.
   Placed last and WITHOUT !important on purpose: equal specificity to a class
   selector, so being last is what wins it, while the four places that show an
   element with an inline style.display keep working — !important would beat
   those too and freeze the theme and translation toggles shut. */
[hidden] { display:none; }

/* ---- a popover may not open off the side of a phone ---------------------- */
/* Anchoring alone cannot keep a panel on screen — each side has its own way of
   falling off, and which one bites depends on where the trigger sits. Anchored
   to the start, a 248px panel opening rightward from a chip at x=256 runs to
   504px on a 390px screen; anchored to the end, the same panel hanging off a
   trigger at x=53 starts at -130px. Every trigger now sits hard against the
   rail's or the row's end, so the base end-anchor is right for all of them and
   the ceiling below is what holds on a narrow screen. */
@media (max-width:620px) {
  .pmenu-panel { max-width:calc(100vw - 24px); }
}

/* ---- the row menu, which had the opposite problem ------------------------ */
/* Found by opening it rather than by measuring the page at rest, which is why
   28 pages could read "clean" while the menu every row exists for did not open
   anywhere you could use it.
   1. .rows and .list-card both clip (overflow:hidden, to hold the end rows'
      tint inside the card radius), so the popover a row opens is clipped by the
      card it lives in. On the LAST row this cut the panel by 130px at 1440px —
      a desktop defect, not a phone one: Ignore and Not a lead were simply not
      on the screen. While a menu is open the card stops clipping; the radius
      only has to hold when nothing is open over it.
   2. The panel is anchored to the END of the dots. A phone flip to the start
      was added when the row wrapped and the dots sat at x=122; since the
      phone rows keep the dots at the row's end (x=326 at 390px, the <=720
      .row grid further down) that flip pushed the panel 132px off the RIGHT
      edge (x=326-522, the page slid to 522px) and off the left in RTL. So the
      base end-anchor holds everywhere, and the generic .pmenu-panel ceiling
      above keeps it inside a phone. */
.rows:has(.rowmenu[open]), .list-card:has(.rowmenu[open]) { overflow:visible; }

/* ---- the row's meta line, on a screen it was never measured at ----------- */
/* .row-meta is deliberately one line that fades out rather than being
   guillotined, and on a desktop that is right. At 390px it has 144px to work
   in, so it faded after the category and every badge past it — PARSED, the AI
   score, the source — was off the row at x=629 with no scroller and no way to
   reach it. The AI score is the reason the row is in the list at all. On a
   phone the line wraps instead: nothing is guillotined there either, it just
   takes two lines to say it. */
@media (max-width:620px) {
  .row-meta {
    white-space:normal; flex-wrap:wrap; overflow:visible;
    -webkit-mask-image:none; mask-image:none;
  }
  /* .row-meta > * is flex:none, so the badge strip sized to max-content and
     wrapping inside it could never fire — four badges fitted and the fifth
     still ended at x=476. Given the whole line, it wraps within the row. */
  .row-meta > .row-badges { flex:1 1 100%; flex-wrap:wrap; min-width:0; }
}
/* ---- the first-run tour --------------------------------------------------

   A cut-out spotlight, not a backdrop. .top-bar is z-index:30 and its own
   stacking context, so "raise the highlighted tab above a scrim" cannot work
   — instead one element carries a 9999px shadow and the dim IS that shadow,
   leaving the hole genuinely clear. pointer-events:none on it is load-bearing
   twice over: the page underneath stays usable while the tour is open, and no
   browser test's click is intercepted by an invisible sheet.

   Deliberately no backdrop-filter: blurring here would blur the cut-out too,
   which is the one region that must stay sharp. */
/* Kept even though [hidden] above now covers the general case. That rule is
   deliberately at class specificity and without !important, so anything giving
   #tour a display — an id selector, or a class added later — would outrank it
   and ship a tour that cannot be closed. This one is id + attribute, so it
   wins whatever arrives later. The JS toggles the attribute, never an inline
   display, so the !important cannot freeze anything shut. */
#tour[hidden] { display:none !important; }
#tour-spot {
  position:fixed; z-index:90; pointer-events:none;
  border-radius:var(--radius-sm);
  box-shadow:0 0 0 9999px rgba(0,0,0,.55);
  outline:3px solid var(--acc-ring); outline-offset:2px;
}
@media (prefers-reduced-motion:no-preference) {
  #tour-spot { transition:top .18s ease, left .18s ease,
                          width .18s ease, height .18s ease; }
}
#tour-card {
  position:fixed; z-index:91; width:min(380px, calc(100vw - 24px));
  background:var(--surface); color:var(--fg);
  border:1px solid var(--line-2); border-radius:var(--radius);
  box-shadow:var(--shadow-hi); padding:18px 20px 16px;
}
#tour-card h3 { margin:0 0 8px; font-size:16.5px; letter-spacing:-.015em;
  text-wrap:balance; }
#tour-card p { margin:0 0 14px; color:var(--fg-2); font-size:13.5px;
  line-height:1.6; }
#tour-card form { margin:0; }
/* The counter sits at the far end of the button row, so the row reads
   "where am I" then "what can I do". */
.tour-count { margin-inline-end:auto; color:var(--mut); font-size:12px;
  font-variant-numeric:tabular-nums; align-self:center; }
#tour-card .explain-actions { align-items:center; }
#tour-back[disabled] { opacity:.45; cursor:default; }


/* ---- standalone auth/result pages (login, signup, link-result, problem,
   password reset/forgot) -------------------------------------------------------
   These pages render WITHOUT the app shell, but all six already link this
   stylesheet. The rules below were byte-identical in all six inline <style>
   blocks, so they live here once: a token change reaches every auth page from
   one place. Rules that differ per page ON PURPOSE (card width, padding, the
   heading and margin tuning each page does) deliberately stay inline in that
   page — they are intentional, not drift to reconcile here. */
.signin-wrap { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 32px 20px; background: radial-gradient(900px 420px at 50% -12%, var(--acc-soft), transparent 70%), var(--bg); }
.provider-btn:hover { background: var(--surface-2); border-color: var(--mut); }
.divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.signin-foot { margin-top: 18px; color: var(--mut); font-size: 12.5px; text-align: center; }
.signin-ok { border-left-color: var(--acc); }
.signin-card h1, .signin-card h2 { font-size: 22px; line-height: 1.3; margin: 0 0 10px; letter-spacing: -.01em; }
.signin-card .lead { margin: 0 0 26px; color: var(--fg-2); font-size: 14.5px; line-height: 1.65; }
.result-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.result-actions a.primary { display: inline-flex; align-items: center; justify-content: center; padding: 11px 20px; border-radius: var(--radius-sm); background: var(--acc); color: var(--acc-fg); font-size: 14.5px; font-weight: 600; text-decoration: none; transition: background .12s; }
.result-actions a.primary:hover { background: var(--acc-hi); }
.signin-card input { width: 100%; padding: 10px 12px; border-radius: var(--radius-sm); border: 1px solid var(--line-2); background: var(--bg); color: var(--fg); font-size: 14px; }

/* ============================================================================
   CONSOLE SHELL — the left-rail admin/settings shell.

   One left rail of sections, a right pane showing only the selected one.
   layout.html swaps the customer tab row for this when the model carries
   console=true. Sits inside .wrap, so it inherits the page's max width and
   centring rather than restructuring the whole grid.

   Two different rails share these classes: the platform operator's own
   screens (fragments/consolerail — platform sections, the alert centre,
   source health) and, since 2026-09-15, Account/Organisation/Platform
   (fragments/settingsrail) for everyone else. Nothing here is
   platform-specific; a rule meant for only one of them belongs scoped to
   that fragment's own markup, not added here.
   ========================================================================== */
.console-shell{
  display:grid; grid-template-columns:238px minmax(0,1fr); gap:26px;
  align-items:start;
}
.console-rail{
  /* Below the sticky top bar, like .side.pin: at top:16px the rail's first
     58px (the "‹ Tenders" link) slid under the bar once the page scrolled. */
  position:sticky; top:calc(var(--bar-h) + 16px); display:flex; flex-direction:column; gap:1px;
  padding:10px; background:var(--surface); border:1px solid var(--line);
  border-radius:12px;
}
.console-rail .crgroup{
  font-size:11px; font-weight:600; letter-spacing:.07em; text-transform:uppercase;
  color:var(--mut); padding:14px 10px 5px;
}
.console-rail .crgroup:first-child{ padding-top:4px; }
.console-rail .crback{
  display:flex; align-items:center; gap:10px; padding:8px 10px 14px;
  margin-bottom:8px; border-bottom:1px solid var(--line);
  color:var(--mut); font-weight:500; font-size:13px; text-decoration:none;
}
.console-rail .crback:hover{ color:var(--fg); }
.console-rail .crback svg{ width:14px; height:14px; flex:none; }
.console-rail .critem{
  display:flex; align-items:center; gap:10px; padding:8px 10px;
  border-radius:8px; color:var(--fg-2); font-weight:500; font-size:14px;
  border:1px solid transparent; text-decoration:none;
}
.console-rail .critem:hover{ background:var(--surface-2); color:var(--fg); }
.console-rail .critem svg{ width:16px; height:16px; color:var(--mut); flex:none; }
.console-rail .critem span{ flex:1; }
.console-rail .critem.on{
  background:var(--acc-soft); color:var(--fg); font-weight:600;
  box-shadow:inset 2px 0 0 var(--acc);
}
.console-rail .critem.on svg{ color:var(--acc); }
/* A fold in the settings rail: the summary reads as an item with a chevron,
   its items sit indented to line up with the summary's label. */
.console-rail .crfold{ display:flex; flex-direction:column; gap:1px; }
.console-rail .crfold > summary{ list-style:none; cursor:pointer; user-select:none; }
.console-rail .crfold > summary::-webkit-details-marker{ display:none; }
.console-rail .crfold > summary::after{
  content:""; flex:none; width:6px; height:6px; margin-right:3px;
  border-right:1.5px solid var(--mut); border-bottom:1.5px solid var(--mut);
  transform:rotate(-45deg); transition:transform .15s;
}
.console-rail .crfold[open] > summary::after{ transform:rotate(45deg); margin-top:-3px; }
.console-rail .crfold > summary.has-on{ color:var(--fg); }
.console-rail .crfold > summary.has-on svg{ color:var(--acc); }
.console-rail .critem.crsub{ padding-left:36px; font-size:13.5px; }
.feed-latest-title{ margin-top:28px; }
.console-rail .crn{
  flex:none; font-size:11.5px; font-weight:600; color:var(--mut);
  background:var(--surface-2); border:1px solid var(--line);
  border-radius:999px; padding:1px 8px; min-width:20px; text-align:center;
}
.console-rail .crn-bad{ color:var(--badfg); background:var(--bad); border-color:transparent; }
.console-rail .crn-warn{ color:var(--warnfg); background:var(--warn); border-color:transparent; }
.console-pane{ min-width:0; }
.console-pane > main{ display:block; }
/* .form-page and .acct centre themselves with margin-inline:auto for their
   normal home — the full .wrap. Unchanged, that same rule re-centres them a
   SECOND time inside the console pane's own (wider) 1fr column: on a wide
   screen the 760px form ends up floating with a big gap on both sides,
   looking orphaned rather than placed. GitHub's own settings pages don't
   re-centre either — content hugs the rail. Left-align only in this context;
   every other page that uses .form-page/.acct on its own is untouched. */
.console-pane .form-page, .console-pane .acct{ margin-inline:0; }

/* ---- the settings shell: one column, one rhythm (2026-09-15 audit) --------
   The audit measured four different right edges across six settings pages —
   760px forms, 1274px tables, a 732px usage table, 809/720px feed forms — so
   every page read as its own layout inside the same rail. One column now:
   everything in the settings shell ends at the same 960px edge, forms and
   account rows fill it rather than capping themselves at 760, and the product
   tiles narrow their minimum so three still fit across. .settings-shell is
   set only on the Account/Organisation shell (layout.html), never on the
   platform console, whose wide operator tables need the full pane. */
.settings-shell .console-pane > main { max-width:960px; }
.settings-shell .form-page, .settings-shell .acct { max-width:none; margin-inline:0; }
.settings-shell .tiles { grid-template-columns:repeat(auto-fill, minmax(min(290px, 100%), 1fr)); }
/* The rail's top edge moved with the page: 120px under a title with no
   subtitle, 145px under one with. Reserving the subtitle's line keeps the
   rail and the first card at the same height on every page. */
.page-head:has(+ .settings-shell) { min-height:58px; }
/* The subtitle ends where the content column ends (238px rail + 26px gap +
   960px pane), instead of running on past it across the whole window. */
.page-head:has(+ .settings-shell) .sub { max-width:1224px; }
/* Organisation settings: one form (one Save), several cards, jump links on top.
   It was a single card about 2,800px tall with nothing saying what was on it. */
.settings-form { display:flex; flex-direction:column; gap:16px; }
.settings-form > .hint { margin:0; }
.settings-jump { display:flex; flex-wrap:wrap; gap:8px; }
.settings-card { scroll-margin-top:16px; }
/* The sticky Save bar drawn as a card of its own: aligned with the cards and
   rounded, not a bare full-width strip floating across them while scrolling. */
.settings-form > .form-actions { margin-top:0; padding:12px 20px;
  border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm);
  position:static; display:flex; align-items:center; justify-content:flex-end; gap:12px; }
/* Nothing changed: Save waits at the end of the form. Something changed: the
   bar follows the reader down (and up) the page and says why it is there. */
.settings-form > .form-actions.dirty { position:sticky; bottom:12px; z-index:5;
  border-color:color-mix(in srgb, var(--acc) 45%, var(--line)); box-shadow:var(--shadow); }
.settings-form > .form-actions .unsaved { display:none; color:var(--fg-2);
  font-size:var(--t-sub); margin-inline-end:auto; }
.settings-form > .form-actions.dirty .unsaved { display:inline; }
.settings-form + .sub { margin-top:var(--s5); }
/* Two cards on /account touched (215px bottom edge, 217px next top) and read
   as one double border. */
.acct > .acct-card + .acct-card { margin-top:var(--s5); }

/* The invite form on /org/members: one card with the sentence that says what
   happens next, instead of a bare inline row narrower than the tables below. */
.invite-form { display:flex; gap:8px; flex-wrap:wrap; align-items:center; }
.invite-form input[type=email] { flex:1 1 260px; min-width:0; }
.invite-card .field-help { margin:10px 0 0; }

/* ---- billing -------------------------------------------------------------
   Six sections, each a card, each answering one question (see the comment at
   the top of org-billing.html). Built from the tokens every other card uses;
   nothing here introduces a colour. */
.billing .h-section { margin:0 0 10px; }
.bill-section { margin-top:28px; }
/* Stacked, not side by side: the sentence sat in a squeezed right-hand column
   and left its last word alone on a line in every state. */
.bill-summary { display:flex; flex-direction:column; align-items:stretch;
  gap:10px; padding:20px 22px; }
.bill-eyebrow { font-size:var(--t-label); font-weight:700; letter-spacing:.085em;
  text-transform:uppercase; color:var(--mut); margin:0 0 6px; }
.bill-plan { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.bill-plan-name { font-size:22px; font-weight:700; letter-spacing:-.02em; line-height:1.2; }
.bill-price { margin-top:4px; color:var(--fg-2); font-size:var(--t-sub); }
/* No flex-basis: in the stacked summary it would be a HEIGHT, not a width. */
.bill-when { max-width:72ch; color:var(--fg-2); font-size:var(--t-sub); line-height:1.55; }
.bill-when p { margin:0; }
.bill-summary .bill-off { flex-basis:100%; margin:0; }
.bill-callout { display:flex; align-items:center; gap:14px; flex-wrap:wrap; margin:16px 0 0; }
.bill-callout > span { flex:1 1 280px; }
.bill-pending { border-inline-start:3px solid var(--acc); }

/* Three meters (seats, products, document storage) since the AI-draft meter
   went with the feature (2026-09-22): three across fits every width above the
   phone breakpoint, where the grid below stacks them. */
.usage-grid { display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); padding:0; }
.usage { padding:16px 20px; min-width:0; }
.usage + .usage { border-inline-start:1px solid var(--line); }
.usage-top { display:flex; justify-content:space-between; align-items:baseline; gap:10px;
  font-size:var(--t-sub); color:var(--fg-2); margin:0 0 10px; }
.usage-top b { font-size:15px; color:var(--fg); font-variant-numeric:tabular-nums; white-space:nowrap; }
.meter { height:6px; border-radius:999px; background:var(--surface-3); overflow:hidden; }
.meter > span { display:block; height:100%; border-radius:inherit; background:var(--tide); }
.meter.full > span { background:var(--alert); }

.plan-cards { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:14px; }
.plan-card { display:flex; flex-direction:column; gap:14px; padding:18px 20px; }
.plan-card.is-current { border-color:var(--acc); box-shadow:0 0 0 1px var(--acc-ring); }
/* Arrived from "Upgrade to Team" (the limit email, the list's limit note). */
.plan-card:target { border-color:var(--acc); box-shadow:0 0 0 3px var(--acc-ring); scroll-margin-top:96px; }
.plan-card-head { display:flex; justify-content:space-between; align-items:flex-start; gap:10px; }
.plan-card-name { font-size:17px; font-weight:680; letter-spacing:-.01em; }
.plan-card-head .sub { margin:2px 0 0; }
.plan-card-price b { display:block; font-size:18px; font-weight:700; letter-spacing:-.01em; }
.plan-card-price .sub { margin:2px 0 0; }
.plan-card-limits { list-style:none; margin:0; padding:12px 0 0; border-top:1px solid var(--line);
  display:grid; gap:8px; }
.plan-card-limits li { display:flex; justify-content:space-between; gap:12px;
  font-size:var(--t-sub); color:var(--fg-2); }
.plan-card-limits b { color:var(--fg); font-variant-numeric:tabular-nums; }
/* Top-aligned: pushed to the card's bottom, a card with one button sat its
   button a row lower than its neighbour's first. */
.plan-card-actions { display:flex; flex-direction:column; gap:8px; }
.plan-card-actions button { width:100%; justify-content:center; }
.plan-change { display:flex; flex-direction:column; gap:6px; margin:0; }
.plan-card-actions .blocked, .bill-row-what .blocked { margin:0; color:var(--alert); font-size:12.5px; line-height:1.45; }
.bill-row-what .blocked { display:block; margin-top:6px; }
/* A disabled action stays readable: it is there to say what exists. */
.billing button:disabled { opacity:.55; cursor:not-allowed; }
.bill-row-acts { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.bill-rows { padding:0; }
.bill-rows > .bill-row + .bill-row { border-top:1px solid var(--line); }
.bill-invoices { padding:0; overflow:hidden; }
.bill-invoices .table { margin:0; }
.bill-nowrap, .bill-pdf { white-space:nowrap; }
.bill-pdf { text-align:right; }
.billing > .bill-section > .bill-reason { margin:8px 2px 0; }
.billing .notes { margin:10px 2px 0; display:flex; flex-direction:column; gap:4px; }
.billing .notes .field-help { margin:0; }
/* -2px undoes .notes' side margin, which is for the help lines: a boxed note
   sat 2px inside the edges of the cards above it. */
.billing .notes .hint { margin:6px -2px 0; }

.bill-row { display:flex; align-items:center; justify-content:space-between; gap:20px;
  flex-wrap:wrap; padding:16px 20px; }
.bill-row-what { flex:1 1 320px; min-width:0; color:var(--fg-2); font-size:var(--t-sub); line-height:1.5; }
.bill-card-onfile { display:block; margin-top:4px; color:var(--fg); font-weight:600; }
.bill-inv-note { display:block; margin:2px 0 0; font-size:12px; }
.bill-row .acct-fold { display:flex; flex-direction:column; align-items:flex-end; }
.bill-row .acct-fold form { margin-top:10px; }
.billing > .bill-section > .field-help { margin:8px 2px 0; }
.bill-help { margin:32px 0 0; padding-top:16px; border-top:1px solid var(--line);
  color:var(--mut); font-size:var(--t-sub); display:flex; gap:8px; flex-wrap:wrap; align-items:center; }

@media (max-width:720px) {
  .usage-grid, .plan-cards { grid-template-columns:1fr; }
  .usage + .usage { border-inline-start:0; border-top:1px solid var(--line); }
  .bill-row .acct-fold { align-items:flex-start; }
  .bill-row-acts { width:100%; }
  .bill-row-acts > form, .bill-row-acts > button { flex:1 1 180px; }
  .bill-row-acts button { width:100%; justify-content:center; }
  /* Invoices as stacked rows: date, plan, amount and status wrap, the PDF
     link keeps the right edge. */
  .bill-invoices .table tr:first-child { display:none; }
  .bill-invoices .table tr { display:flex; flex-wrap:wrap; align-items:center; gap:4px 12px;
    padding:10px 16px; border-bottom:1px solid var(--line); }
  .bill-invoices .table tr:last-child { border-bottom:0; }
  .bill-invoices .table td { display:block; padding:0; border:0; }
  .bill-invoices .table td.bill-pdf { margin-inline-start:auto; }
}

@media (max-width:820px){
  .console-shell{ grid-template-columns:1fr; gap:16px; }
  .console-rail{ position:static; flex-direction:row; flex-wrap:wrap; }
  .console-rail .crgroup{ width:100%; padding:8px 10px 2px; }
  .console-rail .critem{ flex:0 0 auto; }
  .console-rail .critem span{ flex:0 0 auto; }
  /* A fold stays a column on a phone: chevron at the right edge, its items
     full width and indented under the summary's label. */
  .console-rail .crfold{ width:100%; }
  .console-rail .crfold .critem{ flex:none; }
  .console-rail .crfold .critem span{ flex:1; }
}

/* "New tenders have arrived" — the thing that replaced an automatic reload.
   It sits in the flow rather than fixed over the list: a floating pill on a
   phone covers a row, and this is an offer, not an alarm. */
.newrows { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin:0 0 12px; }
/* Declared after the global [hidden] rule with the same specificity, the
   display:flex above won and the banner showed on EVERY list page from the
   first render, an org with no products included. It is news only once the
   poll unhides it. */
.newrows[hidden] { display:none; }
.newrows button { flex:none; }

/* ---- the live status line under the list headings (fragments/syncline) ----
   One quiet line, never a banner: a grey dot and "Updated 14m ago · next check
   in ~16 min" at rest; an accent dot, the counts and a 3px bar while a run is
   going. The bar is a native <progress>, so the page needs no inline width and
   the indeterminate state (a judge pass whose total is not known yet) is the
   element's own. Wraps rather than truncates on a phone: the counts are the
   point. */
.syncline { display:flex; align-items:center; gap:6px 8px; flex-wrap:wrap;
  margin:10px 0 14px; font-size:12.5px; line-height:1.45; color:var(--mut); }
.syncline[hidden] { display:none; }
.syncline .syncdot { width:7px; height:7px; border-radius:50%; flex:none;
  background:var(--line-2); }
.syncline.on .syncdot { background:var(--acc); animation:syncpulse 1.6s ease-in-out infinite; }
.syncline .synctext { min-width:0; overflow-wrap:anywhere; }
.syncline.on .synctext { color:var(--fg-2); }
.syncline .syncbar { -webkit-appearance:none; appearance:none; flex:none;
  width:112px; height:3px; border:0; border-radius:2px; overflow:hidden;
  background:var(--line); color:var(--acc); vertical-align:middle; }
.syncline .syncbar[hidden] { display:none; }
.syncline .syncbar::-webkit-progress-bar { background:var(--line); border-radius:2px; }
.syncline .syncbar::-webkit-progress-value { background:var(--acc); border-radius:2px;
  transition:width .4s ease; }
.syncline .syncbar::-moz-progress-bar { background:var(--acc); border-radius:2px; }
.syncline .syncbar:indeterminate {
  background:linear-gradient(90deg, var(--line) 0%, var(--acc) 50%, var(--line) 100%);
  background-size:200% 100%; animation:syncslide 1.4s linear infinite; }
.syncline .syncbar:indeterminate::-webkit-progress-bar { background:transparent; }
.syncline .syncbar:indeterminate::-moz-progress-bar { background:transparent; }
/* The new-rows offer rides on the same line: "· 7 new tenders  Show them". */
.syncline .newrows { display:inline-flex; margin:0; gap:6px; color:var(--fg);
  font-weight:600; }
.syncline .newrows[hidden] { display:none; }
.syncline .newrows::before { content:"·"; color:var(--mut); font-weight:400; }
.syncline .newrows-go { background:none; border:0; padding:0; margin:0;
  font:inherit; font-weight:600; color:var(--acc); cursor:pointer;
  text-decoration:underline; text-underline-offset:3px; }
.syncline .newrows-go:hover { color:var(--acc-hi); }
/* Put the offer away (fragments/syncline). Quiet, the size of the text. */
.syncline .newrows-x { background:none; border:0; padding:0 4px; margin:0;
  font:inherit; font-weight:400; line-height:1; color:var(--mut); cursor:pointer; }
.syncline .newrows-x:hover { color:var(--fg); }
/* On a phone the bar and the offer wrap under the sentence, and a "·" must
   never begin a line (the scope line learned that first). */
@media (max-width:620px) {
  .syncline .newrows::before { content:none; }
}
@keyframes syncpulse { 0%, 100% { opacity:1; } 50% { opacity:.35; } }
@keyframes syncslide { from { background-position:100% 0; } to { background-position:-100% 0; } }
@media (prefers-reduced-motion: reduce) {
  .syncline.on .syncdot, .syncline .syncbar:indeterminate { animation:none; }
}

/* The solo→team offer on the organisation page: text on the left, one action on
   the right, so the button sits in a fixed place instead of trailing whatever
   the last line of a wrapped sentence left over. It stacks on a phone. */
.upgrade-card { display:flex; align-items:center; gap:18px; margin-top:14px;
  padding:16px 18px; background:var(--surface); border:1px solid var(--line);
  border-left:3px solid var(--acc); border-radius:var(--radius); }
.upgrade-text { display:flex; flex-direction:column; gap:3px; flex:1 1 auto; min-width:0; }
.upgrade-text b { font-size:14.5px; }
.upgrade-text > span { color:var(--fg-2); font-size:13.5px; }
.upgrade-text .upgrade-facts { color:var(--mut); font-size:12.5px;
  font-variant-numeric:tabular-nums; }
.upgrade-go { flex:none; margin:0; }
.upgrade-go button { white-space:nowrap; }
@media (max-width:620px) {
  .upgrade-card { flex-direction:column; align-items:stretch; gap:12px; }
  .upgrade-go button { width:100%; }
}

/* The cookie notice (fragments/cookies.html). Fixed to the bottom, clear of
   the content's own edges on a phone, and above the tour's overlay stack only
   where it has to be. [hidden] is restated: this rule sets display, and it is
   declared after the global [hidden] rule with the same specificity. */
.cookie-notice { position:fixed; left:16px; right:16px; bottom:16px; z-index:900;
  max-width:720px; margin:0 auto; display:flex; align-items:center; gap:14px;
  background:var(--surface); color:var(--fg); border:1px solid var(--line);
  border-radius:var(--radius); box-shadow:var(--shadow); padding:12px 14px 12px 18px;
  font-size:13.5px; line-height:1.5; }
.cookie-notice[hidden] { display:none; }
.cookie-notice p { margin:0; flex:1; color:var(--fg-2); }
.cookie-notice a { white-space:nowrap; margin-left:4px; }
.cookie-notice button { flex:none; padding:8px 18px; }
@media (max-width:560px) {
  .cookie-notice { flex-direction:column; align-items:stretch; }
  .cookie-notice button { width:100%; }
}

/* "View at source" under the detail page's title, beside the source name. */
.detail-head .meta .detail-src { color:var(--acc); font-weight:600; text-decoration:none;
  white-space:nowrap; }
.detail-head .meta .detail-src:hover { text-decoration:underline; }
/* The row menu's one link, set off from the actions above it. */
.pmenu-panel .pmenu-link { display:flex; align-items:center; justify-content:space-between;
  gap:8px; text-decoration:none; border-top:1px solid var(--line); margin-top:4px;
  padding-top:9px; }

/* "Back to site" at the top of the sign-in and sign-up cards. */
.signin-home { display:inline-block; margin:-10px 0 16px; font-size:13px; font-weight:550;
  color:var(--mut); text-decoration:none; }
.signin-home:hover { color:var(--acc); }

/* ── Loading bar and in-place messages (layout.html's swap script) ───────── */
/* A 3px line along the top edge that moves the instant something is pressed,
   so a page waiting on the server never looks like a dead click. */
#loadbar {
  position:fixed; top:0; left:0; z-index:10000;
  height:3px; width:0; opacity:0; pointer-events:none;
  background:var(--acc); box-shadow:0 0 8px var(--acc-ring);
  transition:width .25s ease-out, opacity .3s ease;
}
#loadbar.on { opacity:1; }
#loadbar.done { opacity:0; transition:width .15s ease-out, opacity .3s ease .15s; }
/* The answer to an in-place action, when the page is scrolled away from the
   message line: it floats at the foot of the window for a few seconds. */
#flash.toast {
  position:fixed; left:50%; bottom:20px; transform:translateX(-50%);
  z-index:9000; width:min(560px, calc(100vw - 32px));
  box-shadow:0 10px 30px rgba(0,0,0,.28);
  animation:flash-in .2s ease-out;
}
#flash.toast .hint { margin:0; }
#flash.toast { cursor:pointer; }
@keyframes flash-in { from { opacity:0; transform:translate(-50%, 8px); } }
/* A form waiting on its answer: its button dims instead of taking a second press. */
form[aria-busy="true"] button { opacity:.6; cursor:progress; }
@media (prefers-reduced-motion: reduce) {
  #loadbar, #loadbar.done { transition:none; }
  #flash.toast { animation:none; }
}
#flash:not(:has(*)) { display:none; }
/* The Undo after Ignore / Not a lead: the message, then one quiet button at
   the end of the same line (wrapping under it on a phone). */
.hint.has-undo { display:flex; flex-wrap:wrap; align-items:center; gap:6px 12px; }
.hint.has-undo .flash-msg { flex:1 1 220px; min-width:0; }
.flash-undo { margin:0; flex:none; }
.flash-undo .undo {
  height:30px; padding:0 14px; font-size:13px; font-weight:650; cursor:pointer;
  border-radius:var(--radius-sm); border:1px solid currentColor; background:transparent; color:inherit;
}
.flash-undo .undo:hover { background:color-mix(in srgb, currentColor 10%, transparent); }

/* Why a member action is disabled, under the buttons (org members page). */
.member-why { margin-top:4px; white-space:normal; max-width:36ch; font-size:12px; }

/* Password rules under a new-password field: grey until met, then green. */
.pw-check { list-style:none; margin:8px 0 0; padding:0; display:grid; gap:3px;
  font-size:12.5px; color:var(--mut); }
.pw-check li { display:flex; align-items:center; gap:7px; }
.pw-check li::before { content:""; width:12px; height:12px; flex:none; border-radius:50%;
  border:1.5px solid var(--line-2); }
.pw-check li.ok { color:var(--okfg); }
.pw-check li.ok::before { border-color:var(--okfg); background:var(--okfg);
  box-shadow:inset 0 0 0 2px var(--surface); }
.pw-check li.bad { color:var(--badfg); }
.pw-check li.bad::before { border-color:var(--alert); }
/* The invited address is fixed: it reads as a value, not a field to fill. */
.signin-card input[readonly] { color:var(--fg-2); background:var(--surface-2); cursor:default; }

/* ---- audit E: app UX (2026-09-23) ---- */

/* The cookie notice sat mid-screen as a 720px card over every page's CTAs.
   Now: a compact card in the bottom-left corner on a desktop, a slim bar on
   the bottom edge of a phone, and the page gains that much room at its foot
   while it shows (the script sets --cookie-h), so nothing ends up under it. */
.cookie-notice {
  left:auto; right:auto; inset-inline-start:16px;
  bottom:16px; max-width:440px; margin:0; padding:10px 12px 10px 14px; gap:12px;
  font-size:12.5px; line-height:1.45;
}
.cookie-notice button { padding:6px 14px; }
html.cookie-open body { padding-bottom:var(--cookie-h, 72px); }
@media (max-width:560px) {
  .cookie-notice {
    inset-inline:0; bottom:0; max-width:none; border-radius:0;
    border-width:1px 0 0; flex-direction:row; align-items:center;
    padding:8px 12px; font-size:12px;
  }
  .cookie-notice button { width:auto; }
}

/* Tender rows. Row chips were 9px uppercase — below any reading size — and
   six to eight of them ran off the edge. The template now shows at most two
   tags plus a "+N" (and no "no barrier stated" chip), so the chips can be a
   readable 11px in sentence case. The fade at the line's end is wider, so a
   clipped chip reads as "more here" rather than as a rendering fault. */
.row-badges .pb, .talk-tags .pb {
  font-size:11px; padding:1px 6px; letter-spacing:0; font-weight:600;
  text-transform:none; line-height:1.45;
}
.row-badges .pb-more { font-variant-numeric:tabular-nums; cursor:help; }
.row-badges .pb-ai { cursor:help; }
/* A weak verdict says "Possible fit" in words, and looks unlike a match: an
   outlined chip, not a filled one, so it cannot pass for a green "AI 85". */
.pb-possible { background:transparent; color:var(--warnfg); box-shadow:inset 0 0 0 1px currentColor; }
.row-meta {
  -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 48px), transparent);
          mask-image:linear-gradient(to right, #000 calc(100% - 48px), transparent);
}
[dir="rtl"] .row-meta {
  -webkit-mask-image:linear-gradient(to left, #000 calc(100% - 48px), transparent);
          mask-image:linear-gradient(to left, #000 calc(100% - 48px), transparent);
}

/* How to read a row: the legend popover beside Save search / Export. An icon
   button, so it costs the header nothing; the popover says it in words. */
.rowlegend > summary.btn-sec { display:inline-flex; align-items:center; gap:6px; }
.rowlegend-body {
  position:absolute; inset-inline-end:0; top:calc(100% + 6px); z-index:40;
  width:min(340px, calc(100vw - 32px)); padding:12px 14px;
  background:var(--surface); color:var(--fg-2); border:1px solid var(--line);
  border-radius:var(--radius); box-shadow:var(--shadow); font-size:13px; line-height:1.45;
}
.rowlegend-body p { margin:0; display:flex; gap:10px; align-items:baseline; }
.rowlegend-body p + p { margin-top:10px; }
.rowlegend-body .pb { flex:none; min-width:44px; text-align:center; font-size:11px;
  text-transform:none; letter-spacing:0; }

/* Phone rows: ~240px each, a single title could take four lines and the ⋯
   menu took a line of its own. Now the title stops at two lines (the full
   text is in its title attribute and on the detail page), the country /
   buyer / category line stays one line with the buyer giving way, the chips
   are one line that fades, and the menu sits at the row's right as it does
   on a desktop. */
@media (max-width:720px) {
  .row { grid-template-columns:minmax(0,1fr) auto; }
  .row-acts { padding:0 4px 0 0; flex-direction:column; gap:0; }
}
@media (max-width:620px) {
  .row-ttl {
    white-space:normal; display:-webkit-box; -webkit-box-orient:vertical;
    -webkit-line-clamp:2; line-clamp:2; overflow:hidden;
  }
  .row-meta { white-space:nowrap; flex-wrap:wrap; row-gap:3px; }
  .row-meta > .row-badges {
    flex:1 1 100%; flex-wrap:nowrap; overflow:hidden; min-width:0; margin-inline-start:0;
    -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 32px), transparent);
            mask-image:linear-gradient(to right, #000 calc(100% - 32px), transparent);
  }
  [dir="rtl"] .row-meta > .row-badges {
    -webkit-mask-image:linear-gradient(to left, #000 calc(100% - 32px), transparent);
            mask-image:linear-gradient(to left, #000 calc(100% - 32px), transparent);
  }
  .row-main { padding-block:6px; display:grid; }
  /* The buyer takes whatever the line has left and ellipsises, so country,
     buyer and category stay ONE line and only the chips go to a second. */
  .row-meta > .row-buyer { flex:1 1 0; min-width:4ch; }
  .rowlegend > summary .rowlegend-lbl,
  .page-actions .addown .addown-lbl {
    position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap;
  }
}

/* Inactive sort / per-page options: --mut on the grey track measured 4.42:1,
   under WCAG's 4.5. --fg-2 clears it in both themes; the selected option keeps
   its raised white pill and heavier weight, so the two still differ. */
.sortbar-opt, .sortbar-size { color:var(--fg-2); }

/* The sort strip scrolls sideways on a phone ("Highest…" was simply cut).
   A fade at its trailing edge says there is more to swipe to. */
@media (max-width:620px) {
  .sortbar {
    -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 28px), transparent);
            mask-image:linear-gradient(to right, #000 calc(100% - 28px), transparent);
    padding-inline-end:24px;
  }
  [dir="rtl"] .sortbar {
    -webkit-mask-image:linear-gradient(to left, #000 calc(100% - 28px), transparent);
            mask-image:linear-gradient(to left, #000 calc(100% - 28px), transparent);
  }
}

/* Skip link (layout.html): first focusable element, visible only on focus. */
.skiplink {
  position:absolute; inset-inline-start:12px; top:-60px; z-index:1000;
  padding:10px 16px; border-radius:var(--radius-sm); background:var(--acc); color:var(--acc-fg);
  font-weight:600; text-decoration:none; box-shadow:var(--shadow);
}
.skiplink:focus, .skiplink:focus-visible { top:10px; outline:2px solid var(--fg); outline-offset:2px; }
#main-content:focus { outline:none; }

/* Product form: categories as toggle chips over real checkboxes. The input
   stays in the tab order (visually folded into the chip), so Space toggles
   and the focus ring lands on the chip. */
.togchips { border:0; padding:0; margin:0 0 4px; min-width:0; display:flex; flex-wrap:wrap; gap:6px; }
/* The product form's lock at the plan's product cap: an unstyled wrapper
   while open, visibly inert while disabled (UX audit 2026-10-01). */
.form-lock { border:0; padding:0; margin:0; min-width:0; }
.form-lock:disabled { opacity:.55; }
.togchips > legend { padding:0; margin-bottom:6px; font-size:13px; font-weight:600; color:var(--fg); }
.togchips .togchip, .dform .togchips .togchip { position:relative; display:inline-flex; margin:0;
  cursor:pointer; font-weight:inherit; font-size:inherit; color:inherit; }
.dform .togchips { margin-top:16px; }
.togchip input {
  position:absolute; inset:0; width:100%; height:100%; margin:0; opacity:0; cursor:pointer;
}
.togchip span {
  display:inline-flex; align-items:center; gap:6px; padding:6px 12px; border-radius:999px;
  border:1px solid var(--line-2); background:var(--surface); color:var(--fg-2);
  font-size:13px; font-weight:520; line-height:1.3; transition:background .12s, border-color .12s;
}
.togchip:hover span { border-color:var(--mut); color:var(--fg); }
.togchip input:checked + span { background:var(--acc-soft); border-color:var(--acc); color:var(--fg); font-weight:600; }
.togchip input:checked + span::before { content:"✓"; font-size:12px; color:var(--acc); }
.togchip input:focus-visible + span { outline:2px solid var(--acc); outline-offset:2px; }

/* Product form: the country picker (progressive enhancement over the text field). */
.cpick { display:flex; flex-wrap:wrap; align-items:center; gap:6px; padding:6px;
  border:1px solid var(--line-2); border-radius:var(--radius-sm); background:var(--surface); }
.cpick:focus-within { border-color:var(--acc); box-shadow:0 0 0 2px var(--acc-soft); }
.cpick-chips { display:contents; list-style:none; margin:0; padding:0; }
.cpick-chips li { display:inline-flex; align-items:center; gap:2px; padding:3px 4px 3px 10px;
  border-radius:999px; background:var(--surface-3); color:var(--fg); font-size:13px; }
.cpick-x { border:0; background:none; color:var(--mut); padding:0 6px; min-height:0; font-size:15px;
  line-height:1; cursor:pointer; border-radius:999px; }
.cpick-x:hover, .cpick-x:focus-visible { color:var(--fg); background:var(--surface-2); }
.cpick-find { flex:1 1 180px; min-width:140px; border:0 !important; box-shadow:none !important;
  background:transparent; padding:4px 6px; margin:0; }
.cpick-find:focus { outline:none; }

/* Settings shell on a phone: the rail becomes one "Settings: <here>" switcher
   instead of ~460px of stacked links above the page. */
.crswitch { display:none; }
@media (max-width:820px) {
  .settings-shell .console-rail { padding:0; border:0; background:none; }
  .settings-shell .console-rail > :not(.crswitch) { display:none; }
  .settings-shell .crswitch { display:block; width:100%; position:relative; }
  .crswitch > summary {
    list-style:none; cursor:pointer; display:flex; align-items:center; gap:8px;
    min-height:44px; padding:0 14px; border:1px solid var(--line); border-radius:10px;
    background:var(--surface); color:var(--fg); font-size:14px;
  }
  .crswitch > summary::-webkit-details-marker { display:none; }
  .crswitch > summary .crswitch-lbl { color:var(--mut); font-size:12px; font-weight:600;
    text-transform:uppercase; letter-spacing:.06em; }
  .crswitch > summary svg { margin-inline-start:auto; width:16px; height:16px; color:var(--mut); }
  .crswitch[open] > summary svg { transform:rotate(180deg); }
  .crswitch-list { display:flex; flex-direction:column; margin-top:6px; padding:6px;
    border:1px solid var(--line); border-radius:10px; background:var(--surface); box-shadow:var(--shadow); }
  .crswitch-list a { display:flex; align-items:center; min-height:44px; padding:0 10px;
    border-radius:8px; color:var(--fg-2); text-decoration:none; font-size:14px; }
  .crswitch-list a:hover { background:var(--surface-2); color:var(--fg); }
  .crswitch-list a.on { background:var(--acc-soft); color:var(--fg); font-weight:600;
    box-shadow:inset 3px 0 var(--acc); }
}

/* The first-run checklist at the top of /org (OrgController#setupChecklist). */
.setup-card { margin:0 0 18px; padding:16px 18px; background:var(--surface);
  border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm); }
.setup-head { display:flex; align-items:baseline; justify-content:space-between; gap:12px; flex-wrap:wrap; }
.setup-head h2 { margin:0; font-size:16px; font-weight:650; }
.setup-count { font-size:13px; color:var(--fg-2); font-variant-numeric:tabular-nums; }
.setup-bar { height:6px; margin:10px 0 12px; border-radius:999px; background:var(--surface-3); overflow:hidden; }
.setup-bar > span { display:block; height:100%; background:var(--acc); border-radius:inherit; }
.setup-steps { list-style:none; margin:0; padding:0; display:grid;
  grid-template-columns:repeat(auto-fill, minmax(230px, 1fr)); gap:6px 16px; }
.setup-steps li { display:flex; align-items:center; gap:8px; font-size:13.5px; min-height:28px; }
.setup-tick { flex:none; width:18px; height:18px; border-radius:50%; border:1.5px solid var(--line-2);
  display:inline-flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; }
.setup-steps li.done .setup-tick { background:var(--acc); border-color:var(--acc); color:var(--acc-fg); }
.setup-steps li.done > span:not(.setup-tick) { color:var(--mut); text-decoration:line-through; }
.setup-steps a { color:var(--fg); font-weight:560; }

/* A selected filter chip (Renewals' 6 / 12 / 18 months, its product row) had
   no selected look at all. Same treatment as the pipeline's person chips. */
.actions > a.chip.on {
  border-color:var(--acc); color:var(--acc); background:var(--acc-soft); font-weight:600;
}

/* Disabled OUTLINE buttons (members page: Make member / Deactivate for the
   last manager) were filled grey and read heavier than the live ones beside
   them. A fainter outline on no fill says "not available" instead. */
button.ghost:disabled, button.ghost[disabled], button.danger:disabled, button.danger[disabled] {
  background:transparent; color:var(--mut); border-color:var(--line); opacity:.75;
}

/* Tables that scroll sideways on a phone gave no sign of it: columns simply
   ended at the edge. The script in layout.html marks a scroller that has more
   to its right (data-more), and this fades that edge. */
.table-scroll[data-more] {
  -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 36px), transparent);
          mask-image:linear-gradient(to right, #000 calc(100% - 36px), transparent);
}
[dir="rtl"] .table-scroll[data-more] {
  -webkit-mask-image:linear-gradient(to left, #000 calc(100% - 36px), transparent);
          mask-image:linear-gradient(to left, #000 calc(100% - 36px), transparent);
}

/* Members: past invitations, folded. */
.inv-history { margin-top:10px; }
.inv-history > summary { cursor:pointer; color:var(--fg-2); font-size:13.5px; font-weight:560; padding:6px 0; }
.inv-history .table-scroll { margin-top:6px; }

/* ---- r2 E: UX (2026-09-23) ---------------------------------------------- */
/* The saved search's digest toggle: on = accent envelope, off = faded and
   struck through, so the state reads without hovering. */
.dig-toggle.on { color:var(--acc); }
.dig-toggle.off { opacity:.5; text-decoration:line-through; }

/* Nothing under 11px (UX audit 2): the chip base, the deadline rail's date
   under "Tomorrow", the Social leads dateline chips and unscored labels were
   9-10px, below any comfortable reading size, worst of all on a phone. */
.pb { font-size:11px; }
.row-dl { font-size:11px; }
.row-dl .sub, .sc-none, .intent-pb, .talk-head .pb, .talk-tags .pb, .info-tip { font-size:11px; }
.talk-by, .doc-kind { font-size:11px; }

/* Separators drawn by CSS, never typed. ".dotline > .dots": every item carries
   its own "·" in a gutter to its start; the inner row is pulled back by that
   gutter and the outer one clips it, so the item that begins ANY line — the
   first, or one that wrapped — has its dot clipped away. A typed "·" was left
   dangling at a line's end whenever the meta line wrapped. */
.dotline { display:block; min-width:0; overflow:hidden; flex:1 1 auto; padding-block:2px; }
.dotline > .dots { display:flex; flex-wrap:wrap; align-items:center; row-gap:2px;
  margin-inline-start:-16px; }
.dotline > .dots > * { position:relative; padding-inline-start:16px; }
.dotline > .dots > *::before { content:"·"; position:absolute; inset-inline-start:0;
  width:16px; text-align:center; color:var(--mut); }
.detail-head .meta .dotline .pb { margin-inline-start:6px; }
/* The row's meta line (one line, the buyer gives way): "·" before every item
   but the first, so hiding the category on a phone hides its dot with it. */
.row-meta > .mi + .mi::before { content:"·"; margin-inline-end:6px; }
/* The buyer is dir="auto" (its own script decides), so a ::before inside it
   sits at the start of THAT direction: an Arabic buyer on an English page drew
   its "·" on its right, "Saudi Arabia هيئة التأمين · · Software" (live,
   2026-09-27). Its separator is the country's ::after instead, which follows
   the page's direction like every other dot on the line. */
.row-meta > .mi + .row-buyer::before { content:none; }
.row-meta > .mi:has(+ .row-buyer)::after { content:"·"; margin-inline-start:6px; }
.talk-head > .talk-age::before { content:"·"; margin-inline-end:5px; }

/* Row chips: the tags and "matches <product>" give way (ellipsis, full text
   on hover) so the "+N" and the AI score are never faded or cut off. The
   padding at the line's end keeps the fade over empty space, not over +N. */
.row-meta { padding-inline-end:40px; }
.row-meta > .row-badges { flex:0 1 auto; min-width:0; overflow:hidden; }
.row-badges > * { flex:none; }
.row-badges > .pb-more-m, .row-badges > .pb-more-a { display:none; }
.row-badges > .pb-shrink { flex:0 1 auto; min-width:7ch; max-width:22ch;
  overflow:hidden; text-overflow:ellipsis; }
/* The buyer gives way first (its full name is on hover and on the detail
   page); the tags only once it is down to its floor. */
@media (min-width:621px) { .row-meta > .row-buyer { flex-shrink:6; } }
@media (max-width:620px) {
  /* Phones: country and buyer on one line, then the chips on a line of their
     own — one line, tags shrinking so "+N" stays in view. The category text
     is left out: it is the rail's facet, and there is no room for it. */
  .row-meta { padding-inline-end:0; }
  .row-meta > .row-cat { display:none; }
  .row-meta > .row-badges { flex:1 1 100%; -webkit-mask-image:none; mask-image:none; }
  .row-badges > .pb-product, .row-badges > .pb-tag2, .row-badges > .pb-more:not(.pb-more-m) { display:none; }
  .row-badges > .pb-more-m { display:inline-block; }
  .row-badges:has(> .pb-barrier) > .pb-shrink,
  .row-badges:has(> .pb-barrier) > .pb-more-m { display:none; }
  .row-badges:has(> .pb-barrier) > .pb-more-a { display:inline-block; }
  /* The barrier chip is prose ("reference contracts of comparable size") and
     was flex:none, so a long one pushed the page 6px wider than a 390px phone.
     It gives way like the tags: ellipsis, full text on hover. */
  .row-badges > .pb-barrier { flex:0 1 auto; min-width:7ch;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* The whole-line fade (a desktop rule, later in this file) would still
     fade "+N" at the phone's edge. */
  .row-meta, [dir="rtl"] .row-meta { -webkit-mask-image:none; mask-image:none; }
  [dir="rtl"] .row-meta > .row-badges { -webkit-mask-image:none; mask-image:none; }
  /* Back / Previous / Next on one line: the neighbours become icon buttons
     (their names stay for screen readers), Back keeps its words. */
  .detail-nav { flex-wrap:nowrap; }
  .detail-nav .dn-spacer { display:block; }
  .detail-nav .dn-btn { padding-inline:10px; }
  .detail-nav .dn-btn > span {
    position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; }
}
/* "Translate to" beside the target-language select on the notice text. */
.notice-tools .tl-lbl { font-size:var(--t-sub); color:var(--fg-2); margin-inline-end:6px; white-space:nowrap; }

/* A CLOSED menu's panel takes no room at all. The closed language menu's
   panel was still laid out (to x=484 at 390px), so every page scrolled
   sideways by 6px on a phone. */
details:not([open]) > .pmenu-panel { display:none; }

/* Phones: the sort and per-page strip scrolls sideways; a fade at its end
   says there is more, and the end padding lets the last option scroll clear
   of the fade. */
@media (max-width:720px) {
  .sortbar { padding-inline-end:28px;
    -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 28px), transparent);
            mask-image:linear-gradient(to right, #000 calc(100% - 28px), transparent); }
  [dir="rtl"] .sortbar {
    -webkit-mask-image:linear-gradient(to left, #000 calc(100% - 28px), transparent);
            mask-image:linear-gradient(to left, #000 calc(100% - 28px), transparent); }
}
/* Renewals: value right-aligned in tabular figures, like every money column. */
.table td.num, .table th.num { font-variant-numeric:tabular-nums; }
/* A right-to-left notice text sits at the right of its measure, not floating
   in the middle of the card (dir="auto" resolves it per block). */
.notice-card .body:dir(rtl) { margin-left:auto; }
/* Stacked on a phone, the list's title block takes the full width: with the
   row's align-items:flex-end it hugged the RIGHT edge whenever the title was
   shorter than the screen ("Matched to your products" started at x=60). */
@media (max-width:720px) { .page-head-row > .page-head-main { align-self:stretch; } }
/* "Closed claims hidden: N · Show closed" above the pipeline's rows. */
.closed-claims { margin:0 0 12px; display:flex; gap:8px; flex-wrap:wrap; align-items:baseline; }
.closed-claims a { color:var(--acc); }

/* ======================================================================
   UI review, list pages (2026-09-26). Each rule below answers a measured
   finding on /, /discussions and /set-aside; kept together, at the end, so
   they win over the layers above by position as the v2 layer does.
   ====================================================================== */

/* The search hint starts at the page edge. Its 16px indent dates from when
   the search form sat above it; since the form moved into the top bar the
   hint lined up under the status text (x=44 against 28) and read as the
   status line's second line. */
.searchhint { margin:0 0 14px; }

/* Active filter chips and the selected rail row: the accent TINT says "on",
   the text is ink. Red text on its own tint measured 3.99:1 (chips) and
   4.28:1 (rail) in light, 4.17-4.47:1 in dark: under AA for 12-13px text.
   Same pattern as .console-rail .critem.on and .togchip. The count in a
   selected row takes --fg-2: plain --mut on the tint is still 4.33:1.
   a.chip:hover (0,2,1) used to beat .chip-x:hover, so hovering a chip showed
   red on pink again; this hover is explicit. */
.chip-x { color:var(--fg); }
.chip-x .x { color:var(--acc); opacity:1; }
.activefilters a.chip-x:hover { color:var(--fg); background:var(--acc-soft); border-color:var(--acc); }
.activefilters a.chip-x:hover .x { color:var(--acc); }
.frow.on { color:var(--fg); }
[dir="rtl"] .frow.on { box-shadow:inset -2px 0 0 var(--acc); }
.frow.on .n, .frow.on:hover .n { opacity:1; color:var(--fg-2); }

/* The rail's chevron stays at the row's end, open or shut. Opening a group
   hides the flex:1 selection hint, and the chevron used to jump 112-246px to
   sit against the label. The glyph itself does not turn: mockup A draws the
   same chevron on open and closed groups. */
details.fgroup > summary .chev { margin-inline-start:auto; }

/* ONE phone gutter. The v2 .wrap padding (28px, no media query) sat after the
   phone rule and won, so a 390px page had content at x=28, the logo at 12 and
   the Menu button at 8 — three left edges and 24px lost per row. */
@media (max-width:720px) {
  .wrap { padding:18px 16px 48px; }
  .head { padding:0 16px; }
}
@media (max-width:620px) {
  .tabs-row { padding:0 16px; }
}

/* THE MENU BUTTON LIVES IN THE HEADER ROW on a phone. It sat alone in a second
   sticky row: 52 of the bar's 111px, on screen all the time, for one button.
   Icon-only there (its name stays for screen readers), in the logo mark's
   slot, so the search keeps its width. The checkbox stays beside the nav in
   .tabs-row (the ~ selector needs the siblings); shut, that row is empty. */
@media (max-width:620px) {
  .head .navburger { margin:0; padding:0; width:44px; justify-content:center; flex:none; }
  .head .navburger > span {
    position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap;
  }
  .head .navburger .ic { width:20px; height:20px; }
  .head:has(.navburger) > .logo { display:none; }
  .navtoggle:checked ~ .nav { padding-top:6px; }
}

/* Saved searches: the digest envelope and the delete × were 17-18px wide,
   10px apart, the × destructive. 24px on a desktop, 44px under a thumb (the
   phone height already came from button[type=submit]; the width did not). */
.side .frow .x-del {
  display:inline-flex; align-items:center; justify-content:center;
  min-width:24px; min-height:24px;
}
@media (max-width:620px) {
  .side .frow .x-del { min-width:44px; min-height:44px; }
}

/* The header actions on a phone: the desktop margin-inline-start:auto was
   never reset, and an auto margin in a column turns align-self:stretch off,
   so the buttons floated at x=73 (x=214 on Social leads) under a title at 28.
   The ⓘ keeps its size, the other buttons share the rest of the row, the
   visible summary fills its share, and the legend opens from the ⓘ's side. */
@media (max-width:720px) {
  .page-actions { margin-inline-start:0; }
  .page-actions .rowlegend { flex:none; }
  .page-actions .savesearch > summary { display:flex; justify-content:center; }
  .page-actions .rowlegend-body { inset-inline-start:0; inset-inline-end:auto; }
}

/* THE FILTERS BUTTON (phones).
   - 12px above it and 12px below: its own 12px margin sat on top of the
     24px grid gap (36px below, and the open rail began 36px under its toggle).
   - It shows whether the rail is open: a pressed fill and a chevron that
     turns.
   - The checkbox is focusable: display:none (and the hidden attribute) meant
     a keyboard could never open the rail, and the focus ring rule for it could
     never apply. Visually hidden instead; on a desktop it stays display:none. */
.railtoggle { display:none; }
.railburger .rb-chev { display:none; }
@media (max-width:620px) {
  .layout { row-gap:12px; }
  .railburger { margin:0; }
  .layout > .railtoggle {
    display:block; position:absolute; width:1px; height:1px; margin:0; padding:0;
    opacity:0; pointer-events:none; clip-path:inset(50%);
  }
  .railtoggle:checked + .railburger { background:var(--surface-3); border-color:var(--line-2); color:var(--fg); }
  .railburger .rb-chev { display:block; width:14px; height:14px; margin-inline-start:auto;
    color:var(--mut); transition:transform .15s; }
  .railtoggle:checked + .railburger .rb-chev { transform:rotate(180deg); }
}

/* The list card is the frame. Its empty state drew a dashed box 1px inside
   the card's solid border, and the always-rendered (empty) <ol class="rows">
   brought a second bordered, rounded, shadowed box of its own. */
.list-card .empty { border:0; }
.list-card .rows { border:0; border-radius:0; box-shadow:none; }

/* The row ⋯ on a phone: 30px wide under a thumb, and a tap a few px short
   opened the tender instead. The HIT AREA grows to 44x44 over the row's end
   padding and the last 10px of the row link; the dots and the title width do
   not move. */
@media (max-width:620px) {
  .row-acts .rowmenu > summary { position:relative; }
  .row-acts .rowmenu > summary::after {
    content:""; position:absolute; inset-block:0; inset-inline-start:-10px; inset-inline-end:-4px;
  }
}

/* One case for every chip on a row. The tender rows went to sentence case
   (v2), but the Social leads dateline kept the base .pb capitals ("NEW" beside
   the tender rows' "new"), and so did the translation marker and the intent
   badge. */
.talk-head .pb { text-transform:none; letter-spacing:0; font-weight:600; }
.pb-lang { text-transform:none; letter-spacing:0; }
.intent-pb { text-transform:none; letter-spacing:0; font-weight:600; }

/* The translation marker on a row: an icon in the meta line (the detail page
   keeps the full "Translated from …" chip and its original-title toggle). As a
   chip on its own line it made translated rows 77.5px against 63px (114 vs 91
   on a phone), and most matched tenders are translated. The language it came
   from is in the title, for screen readers too, and in the row legend. */
.row-lang { display:inline-flex; align-items:center; flex:none; color:var(--mut); }
.row-lang .ic { width:13px; height:13px; }

/* "matches: CordonData En…" cut the product name exactly where two products
   differ. The chip keeps shrinking (so "+N" and the score are never cut), it
   just may grow further before it has to. 30ch, not more: every px it takes
   comes out of the buyer (which gives way first), and "matches: CordonData
   Enterprise Do…" already tells the two products apart. */
.row-badges > .pb-product { max-width:30ch; }

/* The selected sort option is raised above its track in both themes (dark
   drew it in --surface, darker than the --surface-3 track: a hole). */
.sortbar-opt:hover, .sortbar-size:hover { background:var(--seg-hover); }
.sortbar-opt.on, .sortbar-size.on { background:var(--seg-on); }

/* The language menu marks the language you are reading in. */
.langmenu .pmenu-panel a.on { color:var(--fg); font-weight:650; }
.langmenu .pmenu-panel a.on::after {
  content:"\2713"; content:"\2713" / ""; margin-inline-start:auto; color:var(--mut); font-weight:600;
}

/* The account menu's utility row is one height: the two buttons (theme, sign
   out) took the global button line-height 1.3 and sat at 30px under two 33px
   links. */
.pmenu-item.pmenu-sub { line-height:1.55; }

/* Red is for the primary action and the current tab. The current PAGE of the
   pager was a solid red block (the heaviest red on the list), the header
   buttons turned red on hover, and the digest envelope was red when on. */
.pager a.cur { background:var(--surface-3); color:var(--fg); border-color:var(--fg-2); font-weight:660; }
.page-actions .btn-sec:hover, .page-actions .savesearch > summary:hover {
  border-color:var(--mut); color:var(--fg); background:var(--surface-3);
}
.dig-toggle.on { color:var(--fg); }

/* The row legend's examples that are not .pb chips keep the chip column. */
.rowlegend-body .score-pill, .rowlegend-body .row-lang { flex:none; min-width:44px; justify-content:center; }
.rowlegend-body .score-pill { font-size:12px; padding:1px 8px; }
/* The category gives way after the buyer and before "+N": a long category
   ("Architecture, construction and engineering…", 146px) pushed "+N" past the
   chip strip's clip on 1 row in 4 at 1024px. Its full name is the rail facet. */
@media (min-width:621px) {
  .row-meta > .row-cat { flex-shrink:3; min-width:6ch; overflow:hidden; text-overflow:ellipsis; }
}

/* ======================================================================
   UI review, detail pages (2026-09-26): the tender and social-lead pages.
   Each rule answers a measured finding on /notice/{id}; kept together, at
   the end, so they win over the layers above by position.
   ====================================================================== */

/* Cards that follow each other in the main column keep a gap. Only headings
   spaced the column, so where two cards met with no heading between them
   (the verdict and the post; the notice text and "What the documents
   require") their borders touched: a 0px gap and one double edge. 16px, the
   composer's own rhythm. */
.detail-main > :is(.card, .surface, .notice-card) + :is(.card, .surface, .notice-card) { margin-top:var(--s4); }

/* The AI summary is one card, not three boxes. .detail .body gave its text
   a second border, padding and fill inside the card: 224px of text on a
   390px phone. Same treatment as the notice text's card. */
.detail .card > .summary-body { background:transparent; border:0; box-shadow:none;
  border-radius:0; padding:0; }

/* "Translated from …" is a quiet chip. It had no hover of its own, so the
   global button:hover painted it brand red under slate text (1.2:1). The
   pressed state keeps its blue while the pointer is on it. */
button.pb-lang:hover { background:var(--neutral); color:var(--fg);
  box-shadow:inset 0 0 0 1px var(--line-2); }
button.pb-lang[aria-pressed="true"]:hover { background:var(--info); color:var(--infofg);
  box-shadow:inset 0 0 0 1px currentColor; }

/* Dialogs keep a 16px gutter on a phone: max-width:420px replaced the
   browser's own inset, so the Claim dialog ran edge to edge at 390px. */
#explain-dialog, #confirm-dialog, #progress-dialog { max-width:min(420px, calc(100vw - 32px)); }
#confirm-dialog { max-width:min(440px, calc(100vw - 32px)); }

/* Red is for the primary action and the current tab. An in-progress stage
   ("Bidding", "Replying") was red text on its own tint (4.28:1 light, 4.17:1
   dark, under AA); it is information, so it takes the info tint. "View at
   source" is a link, not an action: ink and an underline. */
.state-acc { background:var(--info); color:var(--infofg); }
.detail-head .meta .detail-src { color:var(--fg-2); font-weight:600;
  text-decoration:underline; text-underline-offset:3px; white-space:nowrap; }
.detail-head .meta .detail-src:hover { color:var(--fg); text-decoration-thickness:2px; }

/* The Assign / Reassign trigger reads as a control like the select and the
   buttons around it. It inherited the header avatar menu's transparent
   border and pill radius, so it floated as centred text over the rail. */
.facts .rail-form .pmenu > summary.chip { border-color:var(--line-2); border-radius:var(--radius-sm);
  padding:6px 12px; }
@media (min-width:621px) {
  .facts .rail-form .pmenu > summary.chip { min-height:36px; }
}

/* One rule above "More", not two 4px apart. The fold draws it since
   2026-10-02: it now follows the facts list instead of sitting inside it,
   so the last fact is its list's :last-child and draws none. The
   arrow keeps its space on a phone, where the summary turns flex and a
   trailing space in content:"▸ " collapsed ("▸More"). */
.facts-more { border-top:1px solid var(--line); margin-top:0; }
.facts-more > summary::-webkit-details-marker { display:none; }
.facts-more > summary::before { content:"\25B8"; margin-inline-end:.4em; }
.facts-more[open] > summary::before { content:"\25BE"; }
.facts-more:dir(rtl):not([open]) > summary::before { content:"\25C2"; }

/* The Documents card: its empty line kept the paragraph's own 13.5px margin
   on top of the card's padding (28.5px down, 17px in). */
.doc-card > .empty-line { margin:0 0 12px; padding:0; }
/* The file input and its Upload button share one row at 390px too: the
   input kept its 297px native width and pushed Upload onto a row of its own.
   It gives way (capped on a desktop, where it would otherwise run the card's
   width), and its button's gap sits on the logical side for Arabic. */
.doc-upload input[type="file"] { flex:1 1 180px; min-width:0; max-width:420px; }
.doc-upload input[type="file"]::file-selector-button { margin-right:0; margin-inline-end:10px; }

/* ⓘ tips open on a tap. The explanation lived in title= only, which no
   phone ever shows; it is a <details> now, the text in a panel under the
   row. The "i" never takes the uppercase of a dt ("AI MATCH I"). */
.info-tip { text-transform:none; letter-spacing:0; line-height:1; }
details.tip { display:inline-block; vertical-align:middle; position:static;
  text-transform:none; letter-spacing:normal; }
details.tip > summary { list-style:none; cursor:pointer; position:relative; }
/* The circle keeps its 16px under the phone touch rule (details > summary is
   44px there); the hit area grows through ::before instead. */
details.tip > summary.info-tip { min-height:0; display:inline-flex; }
details.tip > summary::-webkit-details-marker { display:none; }
details.tip > summary::before { content:""; position:absolute; inset:-8px; }
details.tip[open] > summary { border-color:var(--fg-2); color:var(--fg); }
.fact, .detail-main > .surface { position:relative; }
details.tip > .tip-body { position:absolute; inset-inline:0; z-index:20; margin-top:6px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm);
  box-shadow:var(--shadow-hi); padding:10px 12px;
  text-transform:none; letter-spacing:normal; font-weight:400; font-size:13px;
  line-height:1.45; color:var(--fg); white-space:normal; text-align:start; }

/* Long notice text folds (layout.html adds .is-folded when the visible text
   is taller than one and a half screens): 3,587px of German sat between a
   phone reader and Documents, Notes and the Timeline. */
/* Translation on top, original below (2026-10-01): each under a small label. */
.notice-card .tl-head { display:flex; flex-wrap:wrap; gap:.35em; align-items:baseline; margin:0 0 .4em; }
.notice-card .tl-orig { margin:0; padding:14px 22px 0; border-top:1px solid var(--line); color:var(--fg-2); font-size:13px; }
.notice-card .tl-error { margin:.5em 0; }
.notice-card.is-folded .body { max-height:24em; overflow:hidden;
  -webkit-mask-image:linear-gradient(#000 75%, transparent); mask-image:linear-gradient(#000 75%, transparent); }
.notice-fold-btn { display:block; margin:10px auto 14px; }
@media print {
  .notice-card.is-folded .body { max-height:none; -webkit-mask-image:none; mask-image:none; }
  .notice-fold-btn { display:none; }
}

@media (max-width:620px) {
  /* Back gives way before Previous/Next do: in German "Zurück zu den
     Ausschreibungen" kept its full width and pushed Next 13px past the
     screen. It shortens with an ellipsis; its full name is the tooltip. */
  .detail-nav > a.chip:first-child { flex:0 1 auto; min-width:0; overflow:hidden; }
  .detail-nav > a.chip:first-child > span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .detail-nav .dn-btn { flex:none; }

  /* The touch rule turns these buttons flex, and flex ignores text-align:
     the rail's Keep / Ignore / Not a lead / Release hugged their left edge
     under a centred Claim. Menu rows stay start-aligned. */
  .btn-solid, .btn-sec, .ghost, .danger { justify-content:center; }
  .pmenu-item, .pmenu-panel a { justify-content:flex-start; }

  /* Detail-page controls the touch rule missed: the buyer's document rows
     (29px), "View at source" (20px), the requirements card's links (17px)
     and the translation chip (18px; its tap area grows, the pill does not). */
  .fact-link { min-height:44px; }
  .detail-head .meta .detail-src { display:inline-flex; align-items:center; gap:.25em; min-height:44px; }
  .req-docs li > a { display:inline-flex; align-items:center; min-height:44px; }
  button.pb-lang { position:relative; }
  button.pb-lang::after { content:""; position:absolute; inset:-13px -4px; }
  details.tip > summary::before { inset:-14px; }

  /* The translate bar: the button is its icon on a phone (its name stays
     for screen readers, and in its title), so "Translate to", the select and
     the button share one row. */
  .notice-tools form .icon-btn { padding-inline:10px; }
  .notice-tools form .icon-btn > span {
    position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; }
}

/* ======================================================================
   UI review, pipeline pages (2026-09-26): My pipeline, Team pipeline,
   Renewals, Outcomes, Activity. Each rule answers a measured finding; kept
   together at the end so they win over the layers above by position.
   ====================================================================== */

/* A table met the block under it with a 0px gap (Renewals' table and the
   "Awarded without a tender notice" card; Outcomes' Buyers table and its
   hint): the rounded table corners sat on the next border. 16px, the
   .hint / .actions rhythm. The pager keeps its own 28px above; a card
   after the pager gets the same 16px. */
main > .table-scroll + .hint, main > .table-scroll + .card, main > .pager + .card { margin-top:var(--s4); }

/* A one-line empty state starts at its heading's edge (it sat 2px in). */
.empty-line { padding-inline:0; }

/* ---- the pipeline stage on a row --------------------------------------- */
/* Tinted by what the stage means (Labels.stageTone): won green, lost red,
   a bid in flight blue, anything else neutral; every stage used to be the
   green of "new" and the AI score, "Lost" included. A thin ring in its own
   ink sets it apart from the flat tag and score chips beside it. */
.pb-info { background:var(--info); color:var(--infofg); }
.pb-stage { flex:none; text-transform:none; letter-spacing:0; font-weight:600;
  box-shadow:inset 0 0 0 1px color-mix(in srgb, currentColor 35%, transparent); }
/* On a desktop the stage is a column by the menu, readable down the list
   (it landed anywhere from x=628 to 1039 in the chip line). The column's
   right edge is the menu, so every chip ends at the same x; short stages
   share a width, so their left edges line up too. */
.row-acts .row-stage { font-size:11px; padding:1px 6px; min-width:9ch; text-align:center; }
.row-badges > .pb-stage-m { display:none; }
@media (max-width:720px) {
  /* ...and on a narrow screen it LEADS the chip line instead: the 34px
     menu column has no room, and as the last chip it was cut ("Biddi") or
     not on the screen at all. */
  .row-acts .row-stage { display:none; }
  .row-badges > .pb-stage-m { display:inline-block; }
}
@media (max-width:620px) {
  /* With the stage on a phone's 202px chip line the tags fold into "+N"
     (the barrier chip's pattern), so the stage, the score and "+N" all stay
     whole: 52-67 + 34 + 40 + 26 + gaps, inside the box. */
  .row-badges:has(> .pb-stage) > .pb-shrink,
  .row-badges:has(> .pb-stage) > .pb-more-m,
  .row-badges:has(> .pb-stage) > .pb-mut:not(.pb-more):not(.pb-shrink):not(.pb-stage) { display:none; }
  .row-badges:has(> .pb-stage) > .pb-more-a { display:inline-block; }
}

/* A short buyer keeps its own width: the 7ch floor (there so a LONG name
   is never squeezed to nothing) left "SECP" 28px before the next "·" where
   other names had 15. A short name never shrinks, so it needs no floor. */
@media (min-width:621px) {
  .row-meta > .row-buyer.mi-short { min-width:0; flex-shrink:0; }
}

/* ---- selected filter chips --------------------------------------------- */
/* Red is for the primary action and the current tab. A selected filter
   chip (Renewals' window and product, the team pipeline's person) was red
   on its tint at 3.99:1 (4.47:1 dark), under AA, and a second "selected"
   look beside the sort bar's raised pill on the same page. One look now:
   the sort bar's. The person's count follows its chip. */
.actions > a.chip.on, .pipeline-who .chip.on, .legal-nav a.chip.on {
  background:var(--surface); color:var(--fg); border-color:var(--fg-2);
  font-weight:600; box-shadow:var(--shadow-sm);
}
.pipeline-who .chip .n { color:var(--fg-2); }
.pipeline-who .chip.on .n { color:var(--fg); background:var(--surface-3); }

/* ---- Renewals ---------------------------------------------------------- */
/* The For column is a product NAME: sentence case, capped, its whole name
   in the tooltip. In the base chip's capitals it was 378px wide and made
   every "Oct 2026" and "View notice" break onto two lines. */
.exp-for .pb { text-transform:none; letter-spacing:0; font-weight:600; max-width:26ch;
  overflow:hidden; text-overflow:ellipsis; vertical-align:middle; }
.table td.nowrap, .table th.nowrap { white-space:nowrap; }
@media (max-width:620px) {
  .table th.hide-phone, .table td.hide-phone { display:none; }
}
/* "Bought through these agreements": the arrow reads as an arrow (it was a
   12px grey dot), and the opened content does not sit on the summary. */
.admin-fold > summary::before { font-size:14px; color:var(--fg-2); }
.admin-fold[open] > summary { margin-bottom:10px; }

/* ---- Activity ---------------------------------------------------------- */
/* Titles in ink, red on hover like every other list's row titles: 16 red
   links on one screen. The log sits in a card. */
.log-card { padding:0 20px; }
.log a { color:var(--fg); font-weight:560; }
.log a:hover { color:var(--acc); }
/* Whether an entry sat beside its time or under it depended on how long
   its text was, so on a phone one entry in sixteen jumped 102px right. A
   fixed basis: beside the time wherever 260px fits, under it everywhere
   on a phone. */
.log .log-what { flex:1 1 260px; }

/* ---- Outcomes ---------------------------------------------------------- */
/* Both stat rows on four equal columns (two on a phone): a row of three
   cards was 429px each under a row of four at 319, and the lone third card
   on a phone stretched to the full width. */
.stats.stats-4 { display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); }
@media (max-width:640px) {
  .stats.stats-4 { grid-template-columns:repeat(2, minmax(0, 1fr)); }
}
/* A breakdown inside a section is a sub-heading, not a peer of the
   section: "By country" was the same 16.5px/660 as "Win rate" above it. */
.h-subsection { font-size:var(--t-sub); font-weight:620; color:var(--fg-2);
  letter-spacing:-.01em; line-height:1.4; margin:0 0 8px; }

/* A page's summary line keeps a reading measure: 199 characters ran the
   full 1310px. (The settings pages' own wider rule is more specific.) */
.page-head .sub { max-width:78ch; }

/* Renewals on a phone: a row is three short lines, ends and value, then
   the buyer, then the incumbent (labelled) and the link. As a table it was
   890px in a 334px scroller, the value and link off screen and a buyer name
   wrapping to four lines in a 120px column. Scoped to .table-stack. */
@media (max-width:620px) {
  .table-stack, .table-stack tbody { display:block; width:100%; }
  .table-stack thead { display:none; }
  .table-stack tr { display:grid; grid-template-columns:minmax(0, 1fr) auto; gap:3px 12px;
    padding:10px 14px; border-bottom:1px solid var(--line); }
  .table-stack tbody tr:last-child { border-bottom:0; }
  .table-stack td { display:block; padding:0; border:0; background:none !important; min-width:0; }
  .table-stack td.c-ends { grid-row:1; grid-column:1; color:var(--fg-2); font-weight:600; }
  .table-stack td.c-val { grid-row:1; grid-column:2; text-align:end; }
  .table-stack td.c-buyer { grid-row:2; grid-column:1 / -1; font-weight:600; color:var(--fg); overflow-wrap:anywhere; }
  .table-stack td.c-held { grid-row:3; grid-column:1; color:var(--fg-2); overflow-wrap:anywhere; }
  .table-stack td.c-held[data-label]::before { content:attr(data-label) " "; color:var(--mut); }
  .table-stack td.c-link { grid-row:3; grid-column:2; text-align:end; }
}

/* ======================================================================
   Organisation pages review (2026-09-26): /org, its settings pages, the
   product page and the product form. Measured at 390 and 1366, light and
   dark; each block says what was wrong.
   ====================================================================== */

/* The identity band on a phone: the text column had no flex basis, so its
   334px natural width could not sit beside the 40px mark and wrapped under
   it — a lone "BT", then the facts, "since …" on a line of its own opening
   with a dangling "·", 130px for four short facts. The text now takes the
   rest of the row; on a phone the facts are one per line (no separator to
   dangle), and the website link keeps its 44px tap area as an overlay
   instead of pushing the line apart. */
.org-id-text { flex:1 1 0; min-width:0; }
@media (max-width:620px) {
  .org-id { flex-wrap:nowrap; align-items:flex-start; }
  .org-id-meta { flex-direction:column; align-items:flex-start; gap:2px; margin-top:0; }
  .org-id-meta > * + *::before { content:none; }
  .org-id-meta .site-link { min-height:0; position:relative; }
  .org-id-meta .site-link::after { content:""; position:absolute; inset:-12px 0; }
}

/* "Delete this organisation" sat flush (0px) under the Save card: the
   intended `.settings-form + .sub` never matched, because the form's dirty-
   state script sits between them. A general sibling holds either way. */
.settings-form ~ .sub { margin-top:var(--s5); }

/* A tile's two stats never fit on one line at any tile width we ship, so the
   28px gap meant for side-by-side was only ever seen as a 28px hole between
   two halves of one group — wider than the gap between tiles. */
.tile-nums { column-gap:28px; row-gap:6px; }

/* The product rule as chips in Inter, not a monospace wall of "OR"s: the
   AI's widened list was 85 terms, 1,624px tall on a phone. */
.rule-row > div:not(.rule-label) { flex:1 1 280px; min-width:0; }
.rule-terms { display:flex; flex-wrap:wrap; gap:6px; list-style:none; margin:0 0 4px; padding:0; }
.rule-terms li { font-size:12.75px; line-height:1.4; color:var(--fg);
  background:var(--surface-2); border:1px solid var(--line); border-radius:var(--radius-xs);
  padding:2px 8px; overflow-wrap:anywhere; max-width:100%; }
.rule-terms-more > summary { cursor:pointer; color:var(--fg-2); font-size:13px; font-weight:560;
  width:fit-content; }
.rule-terms-more[open] > summary { margin-bottom:8px; }

/* Disclosure arrows drawn, not the native marker: the phone touch rule makes
   every summary inline-flex, and an inline-flex summary draws no ::marker —
   "What gets downloaded in the first place" read as a plain red link. The
   disclosure is not the page's primary action, so it is in the text ink. */
.rule-more summary, .rule-terms-more > summary { list-style:none; color:var(--fg-2); }
.rule-more summary::-webkit-details-marker,
.rule-terms-more > summary::-webkit-details-marker { display:none; }
.rule-more summary::before, .rule-terms-more > summary::before {
  content:"▸"; margin-inline-end:6px; font-size:.85em; color:var(--mut); }
[dir="rtl"] .rule-more summary::before, [dir="rtl"] .rule-terms-more > summary::before { content:"◂"; }
.rule-more[open] summary::before, .rule-terms-more[open] > summary::before { content:"▾"; }

/* Members on a phone: each person is a stacked row (the billing invoices'
   pattern) — as a table it was 861px in a 334px scroller with Make member,
   Deactivate and Revoke all off screen. Two actions side by side keep 8px
   between them at every width (they were 4px apart, only the space between
   two inline forms). */
.members-table td.acts { white-space:nowrap; }
.table td > form.inline + form.inline { margin-inline-start:8px; }
@media (max-width:620px) {
  .members-table, .members-table tbody { display:block; width:100%; }
  .members-table tr:first-child { display:none; }
  .members-table tr { display:flex; flex-wrap:wrap; align-items:center; gap:4px 12px;
    padding:12px 16px; border-bottom:1px solid var(--line); }
  .members-table tr:last-child { border-bottom:0; }
  .members-table td { display:block; padding:0; border:0; min-width:0; overflow-wrap:anywhere; }
  .members-table td.claims::before { content:attr(data-label) ": "; color:var(--mut); }
  .members-table td.acts { flex-basis:100%; white-space:normal; display:flex; flex-wrap:wrap;
    align-items:center; gap:8px; }
  .members-table td.acts .member-why { flex-basis:100%; }
  .members-table td > form.inline + form.inline { margin-inline-start:0; }
}

/* Red is for the primary action and the current tab. The setup progress and
   its ticks match the billing meters (tide), the current plan card is marked
   by its badge and a neutral border, and a cross-link between the two feed
   pages is an ordinary underlined link. */
.setup-bar > span { background:var(--tide); }
.setup-steps li.done .setup-tick { background:var(--tide); border-color:var(--tide); color:#fff; }
.plan-card.is-current { border-color:var(--fg-2); box-shadow:none; }
.xlink { color:var(--fg-2); text-decoration:underline; text-underline-offset:2.5px; }
.xlink:hover { color:var(--fg); }
/* A link standing alone in a flex row (a section head, a rule's stats) is a
   phone tap target like any other control: at least 24px tall. */
.section-head > a.xlink, .rule-stats > a.xlink { display:inline-flex; align-items:center;
  min-height:24px; }

/* A chip link that sits beside a full-size button is the button's height:
   Edit next to Retire was 32px beside 36px. (In an .actions row the buttons
   are already chip height — /org/delete's Cancel sits in one.) */
a.chip.chip-btn { padding:8px 14px; font-size:13.5px; line-height:1.3; }

/* The countries picker and the logo input sit on the same fill as every
   other field (they were --surface, 42px, beside 38px --bg fields). */
.cpick { background:var(--bg); padding:3px 6px; min-height:38px; }
.cpick:focus-within { background:var(--surface); }
input[type="file"] { background:var(--bg); }

/* Menu rows: links inherited 1.55 and buttons had 1.3, so one menu was 39px
   rows then 36px rows halfway down. */
.pmenu-panel a, .pmenu-item { line-height:inherit; }

/* The tab count on --surface-3 was 4.42:1 in light (AA needs 4.5). */
.nav a .n { color:var(--fg-2); }

/* The Products head: "+ Add a product" in the buttons' 13.5px (it was
   12.75px beside them), and on a phone it comes before Rescan all and Find
   my products (it had dropped alone to a second row, last). */
.section-head > a.chip { font-size:13.5px; }
@media (max-width:620px) {
  .section-head > a.chip { order:3; }
  .section-head > form { order:4; }
}

/* Feeds: the add form fills its card, lined up with the hints in it. */
.feed-add .feed-form, .feed-request .stacked-form { max-width:none; }
.feed-add { margin-bottom:var(--s5); }
.feed-request { margin-top:var(--s6); }

@media (max-width:620px) {
  /* The touch rule makes these flex, and flex drops the space between an
     icon and its label: "Rescan all" had its icon touching the text. */
  .btn-solid, .btn-sec, .ghost, .danger { gap:6px; }
  /* Tap targets the touch rule missed: checklist links (21px), the lone
     "Delete this organisation" link (16px), the tile and row ⋯ (26 and 30px
     wide). The row rule comes after its 30px base, so it applies. */
  .setup-steps li > a, .form-page > p.sub > a { display:inline-flex; align-items:center; min-height:44px; }
  .tile-menu > summary { min-width:44px; justify-content:center; }
  .row-acts .rowmenu > summary { width:44px; height:44px; }
}

/* ---- account and public pages (UI review 2026-09-26) -------------------- */

/* Honeypots (signup, the contact form): a clipped 1px box with NO offset.
   left:-10000px is scrollable page in a right-to-left document: Arabic
   /signup was 10,390px wide on a phone and showed a blank screen. */
.hp-field, .contact-form .hp {
  position:absolute; top:0; inset-inline-start:0; width:1px; height:1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap;
}

/* The wordmark is a Latin name: on a phone the touch rule makes .brand a
   flex box, and an RTL flex row put "Tide" before "Scout". */
[dir="rtl"] .brand { direction:ltr; unicode-bidi:isolate; }

/* "← Back to site" points the way back in Arabic too. */
.signin-home .arr { display:inline-block; }
[dir="rtl"] .signin-home .arr { transform:scaleX(-1); }

/* Turnstile (interaction-only) draws nothing for most visitors, yet held a
   71px box: collapsed until it asks something (signup.html's tsShow). */
.cf-turnstile { height:0; overflow:hidden; margin:0; }
.cf-turnstile.ts-live { height:auto; overflow:visible; margin-top:14px; }

/* Account folds: an open fold is a right-aligned column, so its button stays
   where it was clicked (it jumped 180px left to the start of the wider
   form). On a phone, where rows read from the left, the fold and its form
   take the row's full width (the fields were 62px short of the others). */
.acct-do > .acct-fold { display:flex; flex-direction:column; align-items:flex-end; }
@media (max-width:620px) {
  /* stretch, not flex-start: the open form sits in the details' content box,
     which flex-start shrank to its content. The chip itself stays a chip. */
  .acct-do > .acct-fold { align-items:stretch; width:100%; }
  .acct-do > .acct-fold > summary { align-self:flex-start; }
  .acct-do > .acct-fold > .acct-pw { width:100%; min-width:0; }
  .acct-pw input { width:100%; min-width:0; }
}

/* One row-action size on /account: a <button class="chip"> took the global
   button line-height (28.6px) and shadow beside 31.8px link chips, and
   hovered solid red. Same look and hover as a.chip; destructive ones take
   the danger fill pair. */
.acct-do button.chip { line-height:inherit; box-shadow:none; }
.acct-do button.chip:hover { border-color:var(--acc); color:var(--acc); background:var(--acc-soft); box-shadow:none; }
.acct-do button.chip.danger-chip:hover { background:var(--bad); color:var(--badfg); border-color:var(--alert); }

/* Tap targets the touch rule missed, on phones: the sign-in footer (15px),
   "Back to site" (20px) and the 404's second link (22px). */
@media (max-width:620px) {
  .signin-foot a { display:inline-flex; align-items:center; min-height:44px; }
  .signin-home { display:inline-flex; align-items:center; min-height:44px; margin:-22px 0 4px; }
  .result-actions a:not(.primary) { display:inline-flex; align-items:center; min-height:44px; }
}

/* ======================================================================
   UI review, Arabic and German (2026-09-26): right-to-left layout and the
   longer German words, measured at 390 and 1366, light and dark.
   ====================================================================== */

/* A Latin title keeps its own reading order (dir="auto") but lines up with
   the page: in Arabic every English row title hugged the card's LEFT edge
   while its badges, buyer and "Translated from" line hugged the right. */
[dir="rtl"] .row-ttl, [dir="rtl"] .talk-ttl, [dir="rtl"] .talk-snip,
[dir="rtl"] .detail-head h1[dir="auto"] { text-align:right; }
/* ...except where the text is line-clamped (a phone's row title, every
   snippet): Chrome draws NO ellipsis on a right-aligned left-to-right clamp,
   so a cut title read as complete ("IMPLEMENTATION OF A"). The box moves to
   the right edge instead and its text keeps its own start alignment. */
[dir="rtl"] .talk-snip { text-align:start; width:fit-content; max-width:100%; margin-left:auto; }
@media (max-width:620px) {
  [dir="rtl"] .row-ttl { text-align:start; width:fit-content; max-width:100%; margin-left:auto; }
}

/* "matches: <product>" is a label and a name in two boxes: only the name is
   cut, at its own end ("يطابق: CordonData Ente…"). As one right-to-left
   string the ellipsis ate the START of the name ("…anagement System"). The
   tags and the buyer carry dir="auto" in the markup for the same reason.
   Desktop only: a phone hides the product chip. */
@media (min-width:621px) {
  .row-badges > .pb-product { display:inline-flex; align-items:baseline; gap:.3em; }
  .pb-product > .pb-lbl { flex:none; }
  .pb-product > .pb-name { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
}

/* The due tile's 3px spine is on the rail's OUTER edge and its hairline
   where it meets the title, in Arabic too (the insets are physical: the
   spine faced the title and the hairline the card's edge). */
[dir="rtl"] .row-dl {
  box-shadow:inset -3px 0 0 color-mix(in srgb, currentColor 72%, transparent),
             inset 1px 0 0 color-mix(in srgb, currentColor 18%, transparent);
}

/* Leading-edge stripes and indents on the reading side in Arabic. The
   settings rail's sub-items were indented from the left (the END in RTL)
   and its current-item stripe sat on the left; its fold chevron pointed
   right. .frow.on stays weaker than its first-row reset (:where). */
[dir="rtl"] .console-rail .critem.crsub { padding-left:10px; padding-right:36px; }
[dir="rtl"] .console-rail .critem.on { box-shadow:inset -2px 0 0 var(--acc); }
[dir="rtl"] .console-rail .crfold > summary::after { margin-right:0; margin-left:3px; transform:rotate(135deg); }
[dir="rtl"] .console-rail .crfold[open] > summary::after { transform:rotate(45deg); }
:where([dir="rtl"]) .frow.on { box-shadow:inset -2px 0 0 var(--acc); }
.upgrade-card { border-left:1px solid var(--line); border-inline-start:3px solid var(--acc); }
@media (max-width:620px) {
  [dir="rtl"] .nav a.on { box-shadow:inset -3px 0 var(--acc); }
}
@media (max-width:820px) {
  [dir="rtl"] .crswitch-list a.on { box-shadow:inset -3px 0 var(--acc); }
}

/* The native file button's gap on the reading side everywhere (feedback,
   product form and settings too, not only the Documents card). */
input[type="file"]::file-selector-button { margin-right:0; margin-inline-end:10px; }

/* Arabic micro-labels: uppercase does nothing in Arabic and tracking pulls a
   joined script apart, so the eyebrow, sort label, filter-group titles,
   facts-rail labels and table heads were 10.5-11px of spaced-out glyphs.
   Arabic gets no tracking, no case change and a size it can be read at. */
html[lang="ar"] :is(.lbl, .sortbar-lbl, .fact dt, .page-head .eyebrow, .table th, .bill-eyebrow) {
  letter-spacing:0; text-transform:none; font-weight:600; font-size:12px; }
html[lang="ar"] :is(.page-head .eyebrow, .sortbar-lbl) { font-size:12.5px; }

@media (max-width:620px) {
  /* The status sentence shares its dot's line and wraps beside it; the bar
     and the new-rows offer drop to the next row. A long German sentence
     wrapped onto a line of its own and left the dot alone above it. */
  .syncline .synctext { flex:1 1 calc(100% - 15px); }

  /* "52,634 set aside by AI" was a 21px tap target. The hit area grows; the
     scope line does not. */
  .scope-line a.aside { display:inline-block; padding-block:6px; margin-block:-6px; }
}

/* ---- platform guides review queue (platform-guides.html, platform-guide.html) ---- */
.gd-status { display:inline-block; padding:2px 8px; border-radius:999px; font-size:12px; font-weight:650;
  letter-spacing:.03em; text-transform:uppercase; background:var(--neutral); color:var(--neutralfg);
  vertical-align:middle; margin-inline-end:6px; }
.gd-draft { background:var(--warn); color:var(--warnfg); }
.gd-approved, .gd-published { background:var(--ok); color:var(--okfg); }
.gd-failed { background:var(--bad); color:var(--badfg); }
.guides-generate { padding:16px 18px; margin:14px 0 8px; max-width:860px; }
.guides-generate form { gap:10px; align-items:center; flex-wrap:wrap; }
.guides-generate p { margin:10px 0 0; }
.guides-table td { vertical-align:top; }
.guides-table td .dim { font-size:12.5px; margin-top:2px; }
.gd-links { display:flex; flex-wrap:wrap; gap:8px; margin:10px 0; }
.gd-problems ul { margin:6px 0 0; padding-inline-start:20px; }
.gd-actions { display:flex; flex-wrap:wrap; gap:10px 14px; align-items:center; margin:14px 0; }
.gd-actions form { gap:10px; align-items:center; flex-wrap:wrap; }
.gd-check { display:inline-flex; gap:6px; align-items:center; font-size:14px; }
.gd-edit { display:grid; gap:6px; max-width:980px; margin:18px 0; }
.gd-edit input[type=text], .gd-edit textarea { width:100%; box-sizing:border-box; }
.gd-edit textarea[name=body] { font-family:ui-monospace, SFMono-Regular, Menlo, monospace; font-size:13.5px;
  line-height:1.55; }
.gd-facts { max-width:980px; margin:18px 0; }
.gd-facts summary { cursor:pointer; font-weight:600; }
.gd-facts ul { margin:8px 0; padding-inline-start:20px; font-size:13.5px; line-height:1.55; }
.gd-facts li { margin:0 0 4px; overflow-wrap:anywhere; }
/* One column width in every status table, so the tables line up. */
.guides-table { table-layout:fixed; width:100%; }
.guides-table th:nth-child(2) { width:150px; }
.guides-table th:nth-child(3) { width:120px; }
.guides-table th:nth-child(4) { width:170px; }
.guides-table th:nth-child(5) { width:96px; }
/* A phone gets the guide, its checks and the Review button. */
@media (max-width:620px) {
  .guides-table th:nth-child(2), .guides-table td:nth-child(2),
  .guides-table th:nth-child(4), .guides-table td:nth-child(4) { display:none; }
  .guides-table th:nth-child(3) { width:78px; }
  .guides-table th:nth-child(5) { width:94px; }
}

/* ── My activity (my-activity.html) and the row marker ─────────────────── */
.myact-scope { margin-top:-6px; }
.myact-sep { width:1px; height:18px; background:var(--line-2); margin:0 4px; }
.myact-list { list-style:none; margin:0; padding:0; display:grid; gap:10px; }
.myact-row { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius, 10px);
  padding:14px 16px; display:grid; gap:6px; min-width:0; }
.myact-top { display:flex; align-items:baseline; gap:12px; min-width:0; }
.myact-title { font-weight:650; color:var(--fg); text-decoration:none; flex:1; min-width:0;
  overflow-wrap:anywhere; }
.myact-title:hover { text-decoration:underline; }
.myact-when { font-size:12.5px; color:var(--mut); white-space:nowrap; font-variant-numeric:tabular-nums; }
.myact-meta { display:flex; flex-wrap:wrap; align-items:center; gap:4px 12px; font-size:12.5px; color:var(--fg-2); }
.myact-closed { color:var(--mut); }
.myact-last { display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 6px; font-size:13.5px; color:var(--fg-2); }
.myact-last b { color:var(--fg); font-weight:600; }
.myact-detail { color:var(--mut); overflow-wrap:anywhere; }
.myact-counts { margin-inline-start:auto; font-size:12.5px; color:var(--mut); font-variant-numeric:tabular-nums; }
.myact-history summary { font-size:12.5px; color:var(--mut); cursor:pointer; }
.myact-history ol { list-style:none; margin:6px 0 0; padding:0 0 0 2px; display:grid; gap:3px;
  font-size:12.5px; color:var(--fg-2); }
.myact-hwhen { display:inline-block; min-width:92px; color:var(--mut); font-variant-numeric:tabular-nums; }
/* On a list row: the viewer's own notes and documents, or that they opened it. */
.row-mine { display:inline-flex; align-items:center; gap:3px; }
.row-mine .ic { width:13px; height:13px; }
.row-mine.worked { color:var(--acc); }
@media (max-width: 640px) {
  .myact-top { flex-direction:column; gap:2px; }
  .myact-counts { margin-inline-start:0; }
  /* One swipeable line per filter row: wrapped, the chips took five rows
     before the first tender. */
  .myact-show, .myact-scope { flex-wrap:nowrap; overflow-x:auto; scrollbar-width:none;
    margin-left:-16px; margin-right:-16px; padding:0 16px 2px; }
  .myact-show::-webkit-scrollbar, .myact-scope::-webkit-scrollbar { display:none; }
  .myact-show .chip, .myact-scope .chip { flex:none; }
}
.myact-more { display:inline-block; margin-top:4px; font-size:12.5px; }

/* "Find on <portal>": the tender's own id beside the link, with a copy button,
   for portals with no page per tender (TenderSource.linksToList). */
.detail-head .meta .detail-src-find { display:inline-flex; align-items:center; gap:6px; flex-wrap:wrap;
  font-size:12.5px; color:var(--fg-2); }
.detail-src-find .src-id { font:600 12.5px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
  background:var(--surface-2); border:1px solid var(--line); border-radius:var(--radius-xs); padding:2px 6px;
  color:var(--fg); overflow-wrap:anywhere; }
.detail-src-find .src-copy { font-size:12px; padding:2px 8px; }
/* Copy as an icon (owner, 2026-10-01): no red button beside every id. */
button.src-copy.icon { background:transparent; color:var(--fg-2); border:1px solid var(--line);
  padding:3px 5px; min-height:0; line-height:0; border-radius:6px; box-shadow:none; }
button.src-copy.icon:hover { color:var(--fg); background:var(--neutral); }
button.src-copy.icon .i-done { display:none; color:var(--okfg, currentColor); }
button.src-copy.icon.copied .i-copy { display:none; }
button.src-copy.icon.copied .i-done { display:inline; }

/* ===============================================================
   UX pass 2026-10-01 (keyboard, tap targets, strips that hid options).
   ====================================================================== */

/* The phone Menu and Filters toggles are <button>s now, so a keyboard reaches
   them (layout.html, data-toggles). They keep the look they had as labels:
   the global button's accent fill, pill, shadow and hover lift are not
   theirs. */
button.navburger, button.railburger { box-shadow:none; letter-spacing:normal; line-height:inherit; }
@media (max-width:620px) {
  button.navburger:hover, button.railburger:hover {
    background:var(--surface-3); color:var(--fg); box-shadow:none; }
  button.navburger:active, button.railburger:active { transform:none; box-shadow:none; }
  button.navburger:focus-visible, button.railburger:focus-visible {
    outline:2px solid var(--acc); outline-offset:2px; }
  button.navburger[aria-expanded="true"] { background:var(--surface-3); color:var(--fg); }
}

/* Strips that hid options with no cue. The sort / per-page bar and the two
   filter rows on /my-activity scrolled sideways on a phone, so "Highest
   value", the page sizes and "Last 90 days" sat off the right edge where
   nothing said they existed (UX review 2026-10-01, owner approved). They
   wrap instead: a second line costs less than an option nobody finds. */
@media (max-width:620px) {
  .sortbar {
    flex-wrap:wrap; overflow-x:visible; row-gap:6px; padding-inline-end:0;
    -webkit-mask-image:none; mask-image:none;
  }
  [dir="rtl"] .sortbar { -webkit-mask-image:none; mask-image:none; }
  .sortbar .seg, .sortbar-seg { flex:0 1 auto; flex-wrap:wrap; max-width:100%; }
  .sortbar-right { margin-inline-start:0; flex:0 1 auto; max-width:100%; }
}
@media (max-width:640px) {
  .myact-show, .myact-scope { flex-wrap:wrap; overflow-x:visible; margin-left:0; margin-right:0;
    padding:0; row-gap:6px; }
}

/* Renewals: "View notice" was a 17px line of text under a thumb. */
@media (max-width:620px) {
  .c-link a { display:inline-flex; align-items:center; min-height:44px; }
}
/* First run (owner, 2026-10-01): with no product yet the upgrade card steps
   back, so "Add your first product" is the loudest thing on /org. */
.upgrade-card.upgrade-quiet { background:transparent; border-inline-start:1px solid var(--line); padding:12px 16px; }
.upgrade-card.upgrade-quiet .upgrade-text b { font-size:13.5px; font-weight:600; }

/* ===============================================================
   Phone list density (new-account E2E 2026-10-01). On a 390px phone the
   first tender began about 700px down: an eyebrow repeating the heading,
   a row of three full-width buttons (the last one a bare "+"), the My
   matches / All tenders switch on a band of its own, the sort chips over
   two lines and the page sizes on a third. Nothing is hidden: the switch
   shares the action row, the buttons are icon squares with their names
   kept for screen readers and in their tooltips, and the sort and page-size
   choices wrap as one block of two lines (the owner's 2026-10-01 rule: wrap,
   never scroll an option out of sight).
   ====================================================================== */
@media (max-width:720px) {
  .page-actions { flex-wrap:wrap; row-gap:8px; }
  .page-actions > .scopeswitch { flex:1 1 180px; min-width:0; margin:0; padding:3px; }
  .page-actions > .scopeswitch a { padding:0 6px; min-height:36px; box-sizing:border-box;
    display:flex; align-items:center; justify-content:center; white-space:nowrap; font-size:13px; }
  .page-actions:has(> .scopeswitch) > .savesearch,
  .page-actions:has(> .scopeswitch) > .btn-sec { flex:none; }
}
@media (max-width:620px) {
  .page-head { margin-bottom:12px; }
  .page-head-row { gap:8px; }
  .page-head-main > .list-eyebrow { display:none; }
  .page-head .scope-line { margin-top:4px; }
  /* Icon squares: the label is read out (and is the tooltip), not drawn. */
  .page-actions .exportmenu > summary .exportmenu-lbl {
    position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap;
  }
  .page-actions .rowlegend > summary,
  .page-actions .exportmenu > summary,
  .page-actions a.addown {
    width:44px; height:44px; min-height:44px; padding:0; justify-content:center; flex:none;
  }
  .page-actions .rowlegend > summary svg,
  .page-actions .exportmenu > summary svg,
  .page-actions a.addown svg { width:17px; height:17px; }
  /* Sort and page size: one grey block that wraps, both segments' options
     flowing together, so "Highest value" and 20 / 50 / 100 share a line. */
  .sortbar { background:var(--surface-3); border-radius:var(--radius-sm); padding:3px;
    gap:2px; row-gap:2px; }
  .sortbar > .sortbar-seg { display:contents; }
  .sortbar-right, .sortbar-right .sortbar-seg { padding:0; gap:2px; background:transparent; }
  .sortbar-opt, .sortbar-size { padding:4px 6px; font-size:13px; }
  .leaving, .page-head .leaving { padding:8px 12px; margin:10px 0 12px; gap:8px; }
  .syncline { margin-bottom:6px; }
}

/* ==== Responsive pass (2026-10-01, owner: "make sure the app is responsive
   and mobile friendly"). Measured by ResponsiveBrowserTest (every page at
   320 / 360 / 390 / 768 / 1024 and in Arabic, each popover opened) and by a
   crawl of production at the same widths. ================================= */

/* Tablets, 621-900px. The list goes to one column below 900px, but the
   Filters button only existed below 620px, so an upright iPad got the whole
   rail (saved searches, closing soon, added, dates...) stacked ABOVE the
   first tender. The phone's drawer now covers the whole one-column range;
   these are the phone rules from the (max-width:620px) blocks above. */
@media (min-width:621px) and (max-width:900px) {
  .layout { row-gap:12px; }
  .layout > aside, .layout > .side { width:100%; position:static; max-height:none; }
  .railburger {
    display:inline-flex; align-items:center; gap:8px; min-height:44px; width:100%; margin:0;
    padding:0 12px; cursor:pointer; user-select:none;
    font-size:14px; font-weight:600; color:var(--fg-2);
    background:var(--surface-2); border:1px solid var(--line); border-radius:8px;
  }
  .layout > .railtoggle {
    display:block; position:absolute; width:1px; height:1px; margin:0; padding:0;
    opacity:0; pointer-events:none; clip-path:inset(50%);
  }
  .railtoggle:not(:checked) ~ .side, .railtoggle:not(:checked) ~ aside { display:none; }
  .railtoggle:focus-visible + .railburger { outline:2px solid var(--acc); outline-offset:2px; }
  .railtoggle:checked + .railburger { background:var(--surface-3); border-color:var(--line-2); color:var(--fg); }
  .railburger .rb-chev { display:block; width:14px; height:14px; margin-inline-start:auto;
    color:var(--mut); transition:transform .15s; }
  .railtoggle:checked + .railburger .rb-chev { transform:rotate(180deg); }
  /* The tab strip ran 36px past a 768px screen, "Renewals" cut mid-word and
     no sign that it scrolls. Without their icons, and a little tighter, a
     team manager's nine tabs fit in English; a longer language still
     scrolls, and the strip's end fades to say so. */
  .nav a svg { display:none; }
  .nav { gap:0; }
  .nav a { padding:7px 6px; font-size:12px; }
  .nav { -webkit-mask-image:linear-gradient(to right, #000 calc(100% - 24px), transparent);
                 mask-image:linear-gradient(to right, #000 calc(100% - 24px), transparent); }
  [dir="rtl"] .nav { -webkit-mask-image:linear-gradient(to left, #000 calc(100% - 24px), transparent);
                             mask-image:linear-gradient(to left, #000 calc(100% - 24px), transparent); }
}

/* iOS Safari zooms the whole page into a field whose text is under 16px,
   and leaves it zoomed after. Fields were 13-14px nearly everywhere: the
   header search, every settings, product, tender, member and account field.
   Phones by width, and any touch screen (an iPad in landscape too). The
   !important is the point: a dozen component rules size their own field
   text, and on a touch screen none of them may win. */
@media (max-width:620px), (hover:none) and (pointer:coarse) {
  input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not([type=submit]):not([type=button]),
  select, textarea { font-size:16px !important; }
}

/* Phones: nothing under 12px. Badges, the date under a row's countdown, the
   "You worked on this" state, table headers and the small caps labels were
   10.5-11.5px: legible on a desktop monitor at arm's length, not on a phone
   in the hand. */
@media (max-width:620px) {
  .pb, .row-dl span, .row-state, .lbl, .sortbar-lbl, .bill-eyebrow, .fact dt,
  .ribbon, .pbm, .talk-where, table th { font-size:12px !important; }
  /* A row's state ("You worked on this", "Kept") was one unbreakable line
     beside the title and took a third of a 320px row from it. It wraps in a
     narrow column at the row's end instead. */
  .row-state { white-space:normal; max-width:7.5em; text-align:end; line-height:1.25; }
}

/* Small phones (an iPhone SE, a 360px Android with large text). */
@media (max-width:420px) {
  /* The header gave the search box 83px at 320 ("S..."): the language menu
     and the avatar each carried words and a chevron. Each is one icon now,
     still 44px to tap; the language menu keeps its spoken name. */
  .head-right { gap:4px; }
  .head-right .langmenu > summary > span,
  .head-right .pmenu > summary > svg:last-child:not(:first-child) { display:none; }
  .head-right .pmenu > summary { min-width:44px; min-height:44px; justify-content:center; padding:4px; }
}
@media (max-width:380px) {
  /* The chips line was one line, clipped: with the tags at their floor the
     "+4" was cut in half at 320. It wraps instead, so every chip is whole. */
  .row-meta > .row-badges { flex-wrap:wrap; row-gap:4px; }
  /* ...but the tag gives up its width first (a zero basis, so it is packed at
     its 7ch floor and then grows into what the line has left); only a chip
     that still does not fit goes to a second line, never a tag on its own. */
  .row-badges > .pb-shrink { flex:1 1 0; }
  /* My matches / All tenders took the row and pushed "+" (add your own
     tender) onto a line of its own. The switch takes the full width, the
     three icon buttons the next line. */
  .page-actions > .scopeswitch { flex:1 1 100%; margin-bottom:0; }
}

/* Social leads rows: the age, the author and the snippet were 11-11.5px. */
@media (max-width:620px) {
  .talk-age, .talk-by, .talk-snip { font-size:12px !important; }
  /* Text-style controls a thumb could miss at 18-20px tall (crawl): the
     closing-today banner's Show them / Keep all, the phone fold's More, and
     the "Translated from ..." chip that swaps in the original title. */
  .leaving .act, .leaving .leaving-keepall .act, .fold-more:not([hidden]), #orig-toggle {
    min-height:24px; display:inline-flex; align-items:center;
  }
}

/* Links that are flex items are blocks, not words in a sentence, so they
   need the 24px of any other control (WCAG 2.5.8). */
.bill-help a { min-height:24px; display:inline-flex; align-items:center; }
