/* ─────────────────────────────────────────────────────────────────────────────────────────────
 * shell.css — ONE portal shell for /account, /account/jobs, /account/links, /account/agents.
 * Served from /account/ (never /assets — that prefix does not reach sn-control).
 *
 * The four :root blocks below are EXTRACTED VERBATIM from PORTAL_PAGE_HTML in server.js, so the
 * palette has one definition even though two files carry it. portalshell.test.js §1 brace-matches
 * all four out of BOTH files and asserts they are byte-identical — every palette edit lands here
 * and in server.js identically, or that suite goes red. (The header used to name a
 * portalshellparity.test.js; no such file has ever existed.)
 *
 * ── 2026-09-08 redesign, SPEC-v2 §2 ──────────────────────────────────────────────────────────
 * The brand is unchanged: --accent keeps #3560d8 / #5b8cff (and #2f57d6 / #6d99ff under v2), --bg
 * keeps #f5f7fb / #070f22, --accent2 keeps the violet family. What changed is what the palette can
 * SAY. Four gradients that lived inside colour tokens (--btnbg x2, --onbbg x2) are now flat fills;
 * --sh2 is `none` because nothing at rest floats; and the roles the old palette could not express
 * — a middle ink, a third rule weight, a seal mark, ink on red, one scrim, one sans stack, one
 * mono stack, the 4px unit, the two radii and the six type sizes — are tokens now instead of 142
 * hand-written literals.
 *
 * ⚠️ ONE DELIBERATE NUMERIC CORRECTION TO SPEC-v2 §2.1. The spec moves --line2 to #c2cee1 / #2b447d
 * with the stated reason "it draws every control border; WCAG 1.4.11 needs >= 3:1 for a UI
 * boundary" (§2.1, restated in §5.1 rule 3 and §6.7). Those two values measure 1.59:1 on #ffffff
 * and 1.78:1 on #0e1c3c — the spec moved the value but did not recompute it, so its own literal
 * misses its own requirement. The requirement wins over the literal: --line2 is #7f8faa light
 * (3.28:1 on --card, 3.05:1 on --bg) and #456ab0 dark (3.15:1 on --card, 3.58:1 on --bg), and
 * --line3 steps up behind it (#5b6c92 = 5.24:1 / #7d9bd8 = 6.04:1) so the three rule weights stay
 * distinguishable. Same reasoning for --on-bad in dark: the spec says #ffffff both themes, but
 * white on the dark theme's #ff7a7a measures 2.53:1, so dark takes the ink side of the pair.
 * ───────────────────────────────────────────────────────────────────────────────────────────── */
:root{color-scheme:light;--bg:#f5f7fb;--bg2:#ffffff;--bgtint:#f5f7fb;--card:#ffffff;--card2:#f2f5fa;--tile:#eef2f9;--field:#ffffff;--tint:#eaf0fb;--ictile:#e6e9fd;--onbbg:#ffffff;--line:#e4e9f2;--line2:#7f8faa;--line3:#5b6c92;--ink:#0c1424;--ink2:#33415c;--mut:#55668a;--faint:#5f6f92;--accent:#3560d8;--accent2:#7c4ff0;--accentTx:#2a4fc4;--btnbg:#3560d8;--onaccent:#ffffff;--bad:#dc2626;--ok:#07713f;--warn:#8a5a10;--okTx:#076c3e;--badTx:#b91c1c;--on-bad:#ffffff;--seal:#e5b94e;--on-seal:#241a05;--okbg:rgba(10,143,82,.10);--okbd:rgba(10,143,82,.32);--badbg:rgba(220,38,38,.08);--badbd:rgba(220,38,38,.30);--warnbg:rgba(183,121,31,.12);--warnbd:rgba(183,121,31,.38);--scrim:rgba(4,7,15,.55);--sh:0 12px 32px rgba(12,20,36,.10);--sh2:none;--sans:"Cipher Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;--u:4px;--row:36px;--gut:24px;--r-ctl:3px;--r-float:8px;--fs-1:12px;--fs-2:12px;--fs-3:12px;--fs-4:13px;--fs-5:14px;--fs-6:20px;--onscrim:#f0f4ff;--onscrimbd:rgba(240,244,255,.45)}
:root[data-theme="dark"]{color-scheme:dark;--bg:#070f22;--bg2:#0b1430;--bgtint:#070f22;--card:#0e1c3c;--card2:#0a1322;--tile:#0a1322;--field:#0a1322;--tint:#16274e;--ictile:#1b2c5c;--onbbg:#101b30;--line:#1c2f57;--line2:#456ab0;--line3:#7d9bd8;--ink:#eef3ff;--ink2:#c3d0ea;--mut:#a3b4d6;--faint:#a3b4d6;--accent:#5b8cff;--accent2:#9366ff;--accentTx:#7fa6ff;--btnbg:#5b8cff;--onaccent:#06101f;--bad:#ff7a7a;--ok:#46d39a;--warn:#ffc24b;--okTx:#46d39a;--badTx:#ff7a7a;--on-bad:#06101f;--seal:#e5b94e;--on-seal:#241a05;--okbg:rgba(70,211,154,.1);--okbd:rgba(70,211,154,.4);--badbg:rgba(255,122,122,.1);--badbd:rgba(255,122,122,.4);--warnbg:rgba(255,194,75,.14);--warnbd:#ffc24b;--scrim:rgba(4,7,15,.72);--sh:0 12px 32px rgba(0,0,0,.45);--sh2:none;--sans:"Cipher Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif;--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;--u:4px;--row:36px;--gut:24px;--r-ctl:3px;--r-float:8px;--fs-1:12px;--fs-2:12px;--fs-3:12px;--fs-4:13px;--fs-5:14px;--fs-6:20px;--onscrim:#f0f4ff;--onscrimbd:rgba(240,244,255,.45)}
:root[data-skin="v2"]{
  --bg:#f4f7fd;--bg2:#ffffff;--card:#ffffff;--tile:#eef3fc;--tint:#eaf0fb;
  --line:#e7ecf6;--line2:#7f8faa;--ink:#0a1220;--mut:#586a8c;
  --accent:#2f57d6;--accent2:#7c4ff0;--accentTx:#2a4fc4;--btnbg:#2f57d6;
  --sh:0 18px 48px rgba(20,32,60,.12);
  --ring:rgba(47,87,214,.22);
}
:root[data-skin="v2"][data-theme="dark"]{
  --bg:#050b1a;--bg2:#0a1330;--card:#0d1a38;--tile:#0a1424;--tint:#16274e;
  --line:#213562;--line2:#456ab0;--ink:#f0f4ff;--mut:#a7b0c4;
  --accent:#6d99ff;--accent2:#9f78ff;--accentTx:#7fa6ff;--btnbg:#6d99ff;
  --sh:0 20px 56px rgba(0,0,0,.55);
  --ring:rgba(109,153,255,.30);
}

/* ── base ──────────────────────────────────────────────────────────────────────────────────── */
*{box-sizing:border-box}
/* Reserve the vertical-scrollbar lane on every portal document. Jobs/Agents can be taller than
   Home; without a stable gutter the entire 248px rail + content frame moves by one scrollbar width
   during a normal navigation even though none of the shell geometry changed. */
html{scrollbar-gutter:stable}
/* SPEC-v2 §3.1/§3.2. Cipher Sans carries the WHOLE product, not just h1-h3: everything a daily user
   actually reads was the system font because the face was scoped to three heading tags. And the
   whole product is a register, so tabular figures are the default rather than four opt-ins. */
body{margin:0;background:var(--bg);color:var(--ink);font:400 var(--fs-4)/1.55 var(--sans);font-variant-numeric:tabular-nums;-webkit-text-size-adjust:100%}
/* Mounted portal documents used to begin at y=0 and gain the 60px header only at DOMContentLoaded.
   Reserve exactly the canonical header lane in their initial HTML. Once mounted, display:contents
   makes .top behave like a body child (important for sticky positioning) while occupying that lane
   with its own identical 60px box. */
#shellhost:empty{display:block;min-height:60px}
#shellhost:not(:empty){display:contents}
/* SPEC-v3 §1 (2026-09-11) — one family, FOUR sizes, three weights, SENTENCE CASE. The six --fs-*
   tokens keep their names (every surface spends them) and now resolve onto the v3 scale:
   --fs-1/-2/-3 = 12 (column heads, captions, counts, meta), --fs-4 = 13 (the reading size),
   --fs-5 = 14 (group and section titles), --fs-6 = 20 (the page title). The 10px/700 uppercase
   +.09em "micro-label" that SPEC-v2 §3.1 made the product's one label idiom is gone everywhere:
   next to Attio's and Linear's real tables it read as a raw database viewer. Weights are
   400/500/600; 700 and 800 belong to the wordmark alone (§1.2). */
h1{font:600 var(--fs-6)/1.25 var(--sans);margin:0 0 8px}
h2{font:600 var(--fs-5)/1.35 var(--sans);margin:0 0 8px}
h3{font:600 var(--fs-4)/1.35 var(--sans);margin:0 0 6px}
button{font:inherit}
/* The UA paints <kbd> in a monospace face. A key legend is not a hash (§1: mono is for hashes only),
   so it reads in the product's own face at the size of the line it sits in. */
kbd{font:inherit}
/* …and the UA's `bold` on <b>/<strong> is 700, a weight SPEC-v3 §1 reserves for the wordmark (which
   uses neither element). MEASURED: plain <b> text was the only 700 left on Home, Records and the
   record. Emphasis in running text is the scale's own top weight. */
b,strong{font-weight:600}
/* §3.3 — anything a person reads out, quotes in an email or types into a search box. Slashed zero
   because these are the strings that must never be ambiguous; unicode-bidi:isolate because a ref, a
   @handle, a time or a +4d delta is an LTR run sitting inside an Arabic or Urdu row (§12.3). */
/* ⛔ direction:ltr JOINS unicode-bidi:isolate, AND ISOLATION ALONE WAS NOT ENOUGH — OBSERVED in
   Arabic at 1440: an isolated run inherits the paragraph direction, so "@leookafor" rendered
   "leookafor@" and the "+4d" overdue delta rendered "4d+". A handle, a ref, a SHA-256, a time and
   a day-delta are LTR strings whatever the page around them is; the neutrals at their edges have
   to resolve LTR too. This is the same pair .brand has carried for the wordmark since day one. */
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums slashed-zero;direction:ltr;unicode-bidi:isolate}
/* SPEC-v3 §1 — mono is for HASHES only now (SHA-256, fingerprints, algorithm ids). A job ref, an
   @handle and a step count read in the product's own face; what they keep from .mono is the bidi
   pair above, because they are still LTR strings inside an Arabic or Urdu row. */
.ltr{direction:ltr;unicode-bidi:isolate}
a{color:var(--accentTx)}
/* ⛔⛔ NEVER HIDE THIS WITH left:-9999px. In a left-to-right page that is off-screen and harmless;
   in Arabic or Urdu the inline start is on the RIGHT, so an element parked 9,999px to the left
   extends the scrollable area — every portal page gained ~10,000px of phantom horizontal scroll in
   both RTL languages, measured at scrollWidth 11264 against a 1265px viewport. The clip technique
   below removes the element from layout entirely, so it is direction-agnostic and adds no overflow,
   while still being reachable by keyboard and announced by a screen reader. */
.skipl{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}
.skipl:focus{position:fixed;width:auto;height:auto;margin:0;clip:auto;clip-path:none;overflow:visible;inset-inline-start:16px;top:16px;z-index:80;background:var(--card);border:1px solid var(--accent);padding:10px 14px;border-radius:var(--r-float);box-shadow:var(--sh)}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--r-ctl)}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* Native navigation remains the source of truth. Supporting browsers keep the shared chrome
   visually continuous across portal documents; every other browser gets the immediate progress
   line and the same direct navigation. No route interception, synthetic history or DOM swapping. */
@view-transition{navigation:auto}
.top{view-transition-name:sn-portal-top}
.pnav{view-transition-name:sn-portal-nav}
:root::after{content:"";position:fixed;inset-block-start:0;inset-inline-start:0;z-index:100;width:0;height:3px;background:var(--btnbg);opacity:0;pointer-events:none;transition:width .18s ease,opacity .12s ease}
:root[data-portal-nav="busy"]::after{width:72%;opacity:1}
::view-transition-old(sn-portal-top),::view-transition-new(sn-portal-top),::view-transition-old(sn-portal-nav),::view-transition-new(sn-portal-nav){animation-duration:.14s;animation-timing-function:ease-out}

/* ── top bar ───────────────────────────────────────────────────────────────────────────────── */
/* SPEC-v2 §9: --card ground, a --line2 bottom rule, NO shadow. The bar is a region boundary, so it
   is drawn with a rule rather than with elevation. */
.top{position:sticky;top:0;z-index:30;display:flex;align-items:center;gap:12px;padding:0 24px;height:60px;background:var(--card);border-bottom:1px solid var(--line2)}
/* ⛔ BRAND LAW (SPEC-v2 §1.2). [crest] · 8px gap · "State" · "Null", 800/17px, Cipher Sans.
   The weight and the size are IDENTITY, not part of §3's three-weight scale, and are exempt from it.
   direction:ltr;unicode-bidi:isolate is load-bearing: without it a flex row under dir="rtl" renders
   the company's own name as "NullState". Only the COLOUR of "Null" moves, and only for the reason
   below. Do not restyle anything else here.

   ⚠️ ONE CORRECTION TO SPEC-v2 §1.2, ON THE OWNER'S BRAND AUTHORITY. That clause turned "Null" from
   a background-clip:text gradient into solid --accent, i.e. BLUE. The marketing site is the brand
   source and it does not: SOURCE marketing/site/index.html:91 sets .brand span to
   linear-gradient(135deg,var(--accent),var(--accent2)) clipped to the text, and its --accent2 is
   #8b5cf6 light / #9366ff dark (lines 44/55) — the same violet as the crest's second gradient stop.
   Flattening the gradient was right; landing on blue was not, and it left one word coloured two
   different ways on two surfaces of one product. "Null" is now the portal's own --accent2, which is
   that violet corrected for text contrast exactly as SPEC-v2 §2.1 already corrected it (#8b5cf6 is
   4.23:1 on white; --accent2 #7c4ff0 is 4.96:1 — same hue to within 1.5°, a shade darker). Violet
   appears NOWHERE else in the portal chrome: it is a brand mark, never an interface accent. */
.brand{display:flex;align-items:center;gap:8px;font:800 17px/1 var(--sans);color:var(--ink);text-decoration:none;direction:ltr;unicode-bidi:isolate}
/* WCAG 2.2 SC 2.5.8 — the wordmark is a LINK to the workspace root and it is a standalone control,
   so it owes the same 24px box `.top nav a.link` was given. MEASURED at every one of eight widths in
   the responsive survey: a.brand renders 111.9 × 18. A separate rule, because the declaration above
   is brand law (§1.2) and its literal does not move for a layout fix. */
.top .brand{min-height:24px}
.brand .mk{width:18px;height:18px;border-radius:var(--r-ctl);background:var(--accent);flex:none}
.brand span{color:var(--accent2)}
.top .sp{flex:1}
/* ⛔ /account's classic inline sheet hides .sp below 560px and relied on #who{margin-inline-start:auto}
   to push the controls to the end of the bar. #who lives INSIDE the account trigger now (SPEC-v3 §5),
   so nothing pushed: SEEN at 393 on /account, the wordmark, Sections and the avatar were packed to the
   start and the account menu opened half off-screen. The spacer does that job at every width, in every
   skin — body + child combinator out-rank the inline sheet's .top .sp without an !important. */
body .top>.sp{display:block}
.pill{font:500 var(--fs-3)/1 var(--sans);color:var(--mut);border:1px solid var(--line2);border-radius:var(--r-ctl);padding:5px 8px}
.tgl,.iconbtn{width:44px;height:44px;min-width:44px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line2);background:var(--card);color:var(--mut);border-radius:var(--r-ctl);cursor:pointer;padding:0}
.tgl:hover,.iconbtn:hover{border-color:var(--accent);color:var(--accentTx)}
.sn-attn-btn{position:relative}
.sn-attn-btn[hidden]{display:none}
/* --on-bad replaces the file's one hard-coded colour (color:#fff on var(--bad)). In dark --bad is a
   PALE red, so the ink side of the pair flips with the theme exactly as --onaccent does. */
.sn-attn-badge{position:absolute;inset-block-start:-3px;inset-inline-end:-4px;display:flex;align-items:center;justify-content:center;min-width:18px;height:16px;padding:0 4px;border:2px solid var(--card);border-radius:var(--r-ctl);background:var(--bad);color:var(--on-bad);font:600 var(--fs-1)/1 var(--sans);font-variant-numeric:tabular-nums}
.sn-attn-badge[hidden]{display:none}
/* ⭐ v4 POLISH (2026-09-11) — THE UNREAD COUNT IS A COUNT, NOT AN ALARM. It was a saturated red square
   inside a 2px ring of the bar's own ground. Red is overdue and nothing else (SPEC-v3 §6.1), and the
   bell counts things that are yours to look at, so under the dense skins it takes the product's
   "yours to act on" blue in both themes, without the ring: --accent under --onaccent measures 6.0:1
   light and 7.1:1 dark. With no ring to separate it from the bell's stroke it sits on the glyph's
   top-END corner (MEASURED at 2x: 5px/3px put its start edge on the bell's centre line and hid half
   the dome), overlapping only the empty corner of the glyph's box. Classic keeps its own badge. */
:root[data-skin="v2"] .sn-attn-badge{inset-block-start:2px;inset-inline-end:-3px;min-width:16px;height:16px;padding:0 4px;border:0;background:var(--accent);color:var(--onaccent)}
.langpick{position:relative;display:inline-flex;align-items:center}
/* background-COLOR, not the `background` shorthand: the shorthand resets background-image, and a
   select with appearance:none has to draw its own chevron there (see ── controls ──). The bug this
   avoids is invisible in getComputedStyle on the select itself — it only shows as a missing arrow. */
.langpick select{height:44px;min-height:44px;background-color:var(--card);color:var(--ink);border:1px solid var(--line2);border-radius:var(--r-ctl);padding-inline:10px 26px;font-size:var(--fs-4)}
.langpick-icon,.skinpick-icon{display:inline-flex;align-items:center;justify-content:center;flex:none;color:var(--mut)}
.langpick-icon svg,.skinpick-icon svg{display:block}
.skinpick{gap:6px}
/* §6.2/§6.3 — blue means "this is yours to act on". The appearance picker is a preference control,
   not a task, so it stops painting itself in the accent just because the v2 skin is on: that was
   blue with no meaning, and on the mobile sheet it made a plain label read as a link. */
:root[data-skin="v2"] .skinpick{color:var(--ink);border-color:var(--line2)}
.shell-quicknav{display:flex;align-items:center;gap:16px;margin-inline-start:8px}
.shell-quicknav .link{display:inline-flex;align-items:center;min-height:24px;white-space:nowrap}
#who,.who{font-size:var(--fs-3);color:var(--mut);white-space:nowrap}

/* ── the band as a composition (SPEC-v2 §9 · §5.1 · §5.6) ────────────────────────────────────
   MEASURED before this pass on /account/links at 1440: eight painted children at FOUR different
   heights — wordmark 18, "Customer Portal" 22, Cloud Vault 24, appearance 44, theme 44, language
   44, notifications 44, Sign out 24 — with 263px of PREFERENCE chrome parked between the wordmark
   and the free space and only 132px of controls on the far side. The row said nothing about what
   mattered: the least-used control in the product (a portal-appearance preference, set once) had
   the best position on every page.

   Two moves, and neither touches the DOM. The header child order nav_vault < skinsel <
   themeToggle < shell_lang < navbtn is pinned by portalshell §6, and paint order has to keep
   matching DOM order or focus order stops matching the eye.

   1. THE SLACK MOVES. `order` alone relocates the flexible spacer from after the language control
      to before the appearance control. Left of it: who you are and where you are. Right of it:
      every control, in one cluster. The left-to-right SEQUENCE is unchanged — only where the gap
      sits — so Tab still walks the row exactly as it reads.
   2. ONE CONTROL HEIGHT, AND A LABEL STOPS PRETENDING TO BE A CONTROL. The controls are 44px
      (pinned literal), so Sign out joins them instead of being a 24px outline among 44px boxes;
      and "Customer Portal" loses its box, because §5.1 lists five permitted borders and a static
      label is not one of them. It keeps the --fs-1 micro-label idiom and takes the region hairline
      (§5.1 rule 2) that separates the name of the company from the name of the surface. */
.top>*{order:2}
.top>a:first-child,.top>.brand,.top>.pill,.top>nav{order:0}
.top .sp{order:1}
.top .pill{display:inline-flex;align-items:center;min-height:24px;border:0;border-radius:0;background:transparent;padding:0;border-inline-start:1px solid var(--line);padding-inline-start:12px}
/* The preference controls recede on INK, never on the boundary: §5.1 rule 3 makes a control border
   information under WCAG 1.4.11, so --line2 stays on all four sides of everything you can operate.
   What changes is that appearance, language and the theme toggle rest in --mut (5.75:1 light,
   8.24:1 dark — comfortably past both the 4.5:1 text bar and the 3:1 glyph bar) while Sign out,
   the only ACTION in the cluster, keeps --ink. */
.top .langpick select{color:var(--mut)}
.top #out{min-height:44px;padding:0 14px}

/* ── THE THREE CONTROLS AND THE ONE ACCOUNT MENU (SPEC-v3 §5, 2026-09-11) ─────────────────────
   The bar used to carry six boxed 44px controls beside the product's one primary action — layout,
   theme, language, bell, @handle, Sign out — and read as a form (OBSERVED, now/pre-home-1440.png).
   It carries three now: the bell, the page's primary action where it has one, and ONE account menu.
   Appearance, theme, language, density and Sign out are set once, not daily, so they sit behind the
   menu; shell.js builds it (mounted pages) or adopts /account's markup, and wires both the same way.
   The controls that stay keep the pinned 44px box and are drawn QUIET — no border and no fill until
   hover — because a row of bordered boxes is exactly what made the header read as a form. WCAG
   1.4.11 is carried by what identifies a borderless icon button: its glyph, --mut on --card (5.75:1
   light, 8.24:1 dark). The focus ring is the global one. */
.top .iconbtn,.top #navbtn{border-color:transparent;background:transparent}
.top .iconbtn:hover,.top #navbtn:hover{border-color:transparent;background:var(--tile);color:var(--ink)}
.top #navbtn{gap:6px;padding:0 10px;color:var(--ink2)}
.navbtn-i{display:inline-flex;color:var(--mut)}
.navbtn-i svg{display:block}
/* Ids, on purpose: /account's inline stylesheet is LATER in source order and still carries classic
   .langpick / .tgl / #who rules. An id outranks every class-built rule in either file, so the menu
   looks the same on all four pages and in every skin without a single !important. */
.acct{position:relative;display:inline-flex;align-items:center;flex:none}
#acct_btn{display:inline-flex;align-items:center;gap:8px;height:44px;min-height:44px;min-width:44px;margin:0;padding:0 8px;border:1px solid transparent;border-radius:var(--r-ctl);background:transparent;color:var(--ink2);font:500 var(--fs-4)/1 var(--sans);white-space:nowrap;cursor:pointer}
#acct_btn:hover{background:var(--tile);color:var(--ink)}
#acct_btn[aria-expanded="true"]{background:var(--tint);color:var(--ink)}
/* SPEC-v3 §0 — an avatar is the one circle the radius law allows (both references draw round faces;
   a square face reads as a bug). The SAME idiom as the queue's "With" column (jobs.html .qav): a
   --line disc with the initial in --ink2 — 8.4:1 light, 8.0:1 dark — so a person looks the same in the
   bar as in the register. A tinted disc was tried first and vanished into the trigger's own --tint
   wash the moment the menu opened (SEEN in the first 1440 render). */
.acct-av{display:inline-flex;align-items:center;justify-content:center;flex:none;width:28px;height:28px;border-radius:50%;border:0;background:var(--line);color:var(--ink2);font:600 var(--fs-4)/1 var(--sans);overflow:hidden}
.acct-av svg{display:block}
/* Never a display value on #who here: the pinned ≤1179 and ≤720 rules hide the handle and only the
   round initial stays. #who is a flex item, so max-width and the ellipsis work without one. */
#acct_btn #who,#acct_btn .acct-note{min-width:0;max-width:176px;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--fs-4);color:inherit}
#acct_btn #who{direction:ltr;unicode-bidi:isolate}
#acct_btn #who:empty,#acct_btn .acct-note:empty{display:none}
.acct-chev{display:inline-flex;flex:none;color:var(--mut)}
.acct-chev svg{display:block}
/* THE ONE FLOATING, SHADOWED SURFACE IN THE PORTAL (§5.4 --sh, §5.3 --r-float). It sits in the DOM
   right after its trigger, so Tab reaches it next; and it is position:absolute, so it can never widen
   the bar — which is what lets Sign out live inside it on a 393px phone (portalshell §4). */
#acct_menu{position:absolute;inset-block-start:calc(100% + 6px);inset-inline-end:0;z-index:40;width:320px;max-width:calc(100vw - 16px);margin:0;padding:6px;background:var(--card);border:1px solid var(--line2);border-radius:var(--r-float);box-shadow:var(--sh);color:var(--ink);font:400 var(--fs-4)/1.4 var(--sans);text-align:start;white-space:normal}
#acct_menu[hidden]{display:none}
#acct_menu .acct-id{display:flex;align-items:center;gap:10px;margin:0 0 6px;padding:6px 8px 10px;border-bottom:1px solid var(--line)}
#acct_menu .acct-id[hidden]{display:none}
#acct_menu .acct-id .acct-av{width:32px;height:32px}
#acct_menu .acct-idt{display:flex;flex-direction:column;gap:2px;min-width:0}
#acct_menu .acct-nm{font-weight:600;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#acct_menu .acct-hd{font-size:var(--fs-3);color:var(--mut);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* One row = the setting's name at the start, its control at the end — the way Linear draws a
   preferences row. The words wrap before the control shrinks: German and Greek labels run long. */
#acct_menu .acct-row,#acct_menu .langpick{display:flex;align-items:center;justify-content:space-between;gap:12px;width:auto;height:auto;min-height:40px;margin:0;padding:2px 8px;border:0;border-radius:var(--r-ctl);background:transparent;color:var(--ink2);font:400 var(--fs-4)/1.3 var(--sans);cursor:pointer}
#acct_menu .acct-row:hover,#acct_menu .langpick:hover{background:var(--tile);color:var(--ink)}
#acct_menu .acct-l{flex:1 1 auto;min-width:0;color:inherit}
#acct_menu .langpick>svg,#acct_menu .langpick-icon,#acct_menu .skinpick-icon{display:none}
#acct_menu select{flex:0 1 auto;width:auto;min-width:0;max-width:58%;height:32px;min-height:32px;margin:0;padding-block:0;padding-inline:8px 24px;border:1px solid var(--line2);border-radius:var(--r-ctl);background-color:var(--field);background-position:right 8px center;color:var(--ink);font:500 var(--fs-4)/1 var(--sans)}
[dir="rtl"] #acct_menu select{background-position:left 8px center}
/* A switch is a 40x24 control (WCAG 2.5.8's 24px), square like every other control (§5.3), and it
   carries its state in two ways — the knob's side and the fill — never in colour alone. Off: a --line2
   boundary (3.28:1 / 3.15:1, §5.1 rule 3) and a --mut knob; on: the --accent fill, --onaccent knob. */
#acct_menu .acct-sw{position:relative;flex:none;width:40px;min-width:40px;height:24px;min-height:24px;margin:0;padding:0;border:1px solid var(--line2);border-radius:var(--r-ctl);background:transparent;cursor:pointer}
#acct_menu .acct-knob{position:absolute;inset-block-start:3px;inset-inline-start:3px;width:16px;height:16px;border-radius:var(--r-ctl);background:var(--mut);transition:inset-inline-start .14s ease,background-color .14s ease}
#acct_menu .acct-sw[aria-checked="true"]{background:var(--accent);border-color:var(--accent)}
#acct_menu .acct-sw[aria-checked="true"] .acct-knob{inset-inline-start:19px;background:var(--onaccent)}
/* Compact rows only mean something to the dense register, which only the v2 skin draws. */
#acct_menu .acct-dense{display:none}
:root[data-skin="v2"] #acct_menu .acct-dense{display:flex}
/* …and a visitor who is not signed in has no register at all (SEEN on the signed-out /account menu). */
:root[data-sn-access="anonymous"] #acct_menu .acct-dense{display:none}
/* Simple mode (WAVE1-SPEC §2 S): the row's name, its one-line meaning under it, the switch at the end; a refused
   change is said in the row's own status line. An account setting, so a signed-out visitor has no row. */
#acct_menu .acct-simple{flex-wrap:wrap}
#acct_menu .acct-simple .acct-l{display:flex;flex-direction:column;gap:2px}
#acct_menu .acct-d{font-size:var(--fs-3);color:var(--mut)}
#acct_menu .acct-st{flex:1 0 100%;font-size:var(--fs-3);color:var(--bad)}
#acct_menu .acct-st:empty{display:none}
#acct_menu .acct-sw[aria-busy="true"]{opacity:.6;cursor:progress}
:root[data-sn-access="anonymous"] #acct_menu .acct-simple{display:none}
#acct_menu .acct-sep{height:1px;margin:6px -6px;background:var(--line)}
#acct_menu .acct-sep[hidden]{display:none}
/* Sign out is the one ACTION in the menu, so it reads in --ink as a full-width item rather than as a
   bordered button. [hidden] is restated because the display value above would otherwise out-rank the
   UA rule before setAccess has written the inline display. */
#acct_menu #out{display:flex;align-items:center;justify-content:flex-start;width:100%;height:auto;min-height:36px;margin:0;padding:0 8px;border:0;border-radius:var(--r-ctl);background:transparent;color:var(--ink);font:500 var(--fs-4)/1.3 var(--sans);text-align:start;filter:none}
#acct_menu #out:hover{background:var(--tile)}
#acct_menu #out[hidden]{display:none}
@media(pointer:coarse){
  #acct_menu .acct-row,#acct_menu .langpick{min-height:48px}
  #acct_menu select{height:36px;min-height:36px}
  #acct_menu #out{min-height:44px}
}

/* ── six-item destination nav (desktop) ────────────────────────────────────────────────────── */
.pnav{display:flex;gap:4px;padding:8px 24px 0;border-bottom:1px solid var(--line);background:var(--card);overflow:visible}
.pnav a{flex:none;color:var(--ink2);text-decoration:none;font-size:var(--fs-4);font-weight:500;padding:8px 12px 10px;border-bottom:2px solid transparent;white-space:nowrap;transition:color .16s ease,border-color .16s ease}
.pnav a:hover{color:var(--ink)}
.pnav a.on{color:var(--ink);border-bottom-color:var(--accent);font-weight:500}
/* v2's horizontal strip: a --tint wash plus the SAME 2px accent rule the rail uses, instead of the
   inset 1px ring. One idiom for "you are here" in both navigation layouts (§9). */
:root[data-skin="v2"] .pnav{border-bottom:0;padding:10px 24px 0}
:root[data-skin="v2"] .pnav a{border-radius:var(--r-ctl);padding:8px 12px 10px}
:root[data-skin="v2"] .pnav a:hover{background:var(--tile);color:var(--ink)}
:root[data-skin="v2"] .pnav a.on{background:var(--tint);color:var(--ink);border-bottom-color:var(--accent);box-shadow:none}

/* ── mobile sections sheet (replaces the horizontal nav strip; never a scrollbar) ──────────── */
#navbtn{position:relative;display:none;align-items:center;gap:8px;height:44px;padding:0 12px;border:1px solid var(--line2);background:var(--card);color:var(--ink);border-radius:var(--r-ctl);font:500 var(--fs-4)/1 inherit;cursor:pointer}
.sheet{position:fixed;inset:0;z-index:70;display:none}
.sheet.open{display:block}
.sheet .scrim{position:absolute;inset:0;background:var(--scrim)}
/* The sheet genuinely floats and can be dismissed, so it is one of the four subjects allowed to use
   --sh (§5.4), and one of the things allowed --r-float (§5.3). Logical insets so it anchors the same
   way under dir="rtl". */
.sheet .panel{position:absolute;inset-inline:0;inset-block-end:0;background:var(--card);border-radius:var(--r-float) var(--r-float) 0 0;padding:8px 12px calc(12px + env(safe-area-inset-bottom));box-shadow:var(--sh);max-height:82vh;overflow:auto}
.sheet h2{margin:10px 8px 6px;font:500 var(--fs-3)/1.4 var(--sans);color:var(--mut)}
.sheet a{display:flex;align-items:center;min-height:52px;padding:0 12px;border-radius:var(--r-ctl);color:var(--ink2);text-decoration:none;font-weight:500}
.sheet a.on{background:var(--tint);color:var(--ink)}
.sheet a:hover{background:var(--tile)}
.shell-sheet-tools{display:none}
.shell-sheet-vault[hidden]{display:none}
/* portal final H-06: the phone's unread number. The sheet's Notifications row carries the bell's badge inline, after
   its word; Sections carries a dot (the number is one tap away, inside). Same fill as the bell's badge in each skin. */
.sn-attn-badge.sn-sheet-badge{position:static;display:inline-flex;flex:0 0 auto;width:auto;margin:0;border:0}
.sn-attn-badge.sn-sheet-badge[hidden]{display:none}
#sheet_attention{display:flex;flex-direction:row;align-items:center;justify-content:center}
.navbtn-dot{position:absolute;inset-block-start:9px;inset-inline-start:21px;width:9px;height:9px;border:2px solid var(--card);border-radius:50%;background:var(--bad)}
.navbtn-dot[hidden]{display:none}
:root[data-skin="v2"] .navbtn-dot{background:var(--accent)}

/* ── page frame ────────────────────────────────────────────────────────────────────────────── */
.wrap{max-width:1180px;margin:0 auto;padding:28px 24px 72px}
/* SPEC-v3 §1 — a section head is a TITLE, 14/600 in ink, sentence case: the size and the weight say
   "a new group starts here", which the 10px uppercase micro-label used to shout instead.
   §5.1 rule 4 (SPEC-v2, still binding) — it spends the ONE --line3 rule on its surface. That single
   darker line is what lets a page group its content without drawing a box around it, and it is the
   same idiom the job record already uses. A head that shares its row with controls moves the rule
   onto the ROW (see .hdrow on /account/agents) so it spans the group rather than the words.
   ⚠️ 2026-09-22: under v2 the rule is gone (SPEC portal-polish A.8) — the CARD below a head is the grouping
   now, so a ruled head on top of a surfaced card drew the group twice. Classic keeps this rule. */
.sec{font:600 var(--fs-5)/1.35 var(--sans);color:var(--ink);margin:20px 0 8px;padding-bottom:6px;border-bottom:1px solid var(--line3)}
/* §5.4 — --sh2 is `none`. Nothing at rest is elevated: when everything floats, nothing does. The
   declaration and the token stay so the parity blocks and every .card reference are intact.
   §5.3 — structure is square; the two radii are spent on controls and on things that float.
   ⚠️ 2026-09-22: SUPERSEDED UNDER THE v2 SKIN by SPEC portal-polish A.4 (the owner asked for the Paddle/Stripe
   look: a card is a surface — 12px corners and a soft two-layer shadow). This base rule is still what CLASSIC
   draws; the v2 card lives in the "portal polish" block at the end of this file. */
.card{background:var(--card);border:1px solid var(--line);border-radius:0;padding:16px;box-shadow:var(--sh2)}
.card+.card{margin-top:12px}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}
.big{font:600 var(--fs-6)/1.2 var(--sans);font-variant-numeric:tabular-nums}
.sm{font-size:var(--fs-3);color:var(--mut)}
/* §6.6 — a tag carries Hold / Agent / Guest, never a status, and never a fill. SPEC-v3 §1: in sentence
   case at 12/500, a small outlined token rather than a shouted one. */
.tag{display:inline-block;font:500 var(--fs-3)/1.4 var(--sans);padding:1px 6px;border-radius:var(--r-ctl);border:1px solid var(--line2);color:var(--mut);background:transparent}
.link{color:var(--accentTx);text-decoration:none;font-weight:500;font-size:var(--fs-3)}
.link:hover{text-decoration:underline}

/* ── expired subscription: one fail-closed surface on every /account route ───────────────────
   account.entitled is learned from authenticated /me after the HTML arrives. While it is unknown,
   product markup is never painted. An explicit false hides every existing page child and exposes
   only the shared subscription card. The legal export/delete exits are deliberately inside that
   card; Records, Proof Requests, Shared Links, Agents, attention and deep-link targets stay hidden. */
:root[data-sn-access="pending"] #main{visibility:hidden}
:root[data-sn-access="locked"] #main>:not(#sn_access_lock),
:root[data-sn-access="unavailable"] #main>:not(#sn_access_lock){display:none!important}
:root[data-sn-access="pending"] body .pnav,
:root[data-sn-access="locked"] body .pnav,
:root[data-sn-access="unavailable"] body .pnav,
:root[data-sn-access="pending"] body #navbtn,
:root[data-sn-access="locked"] body #navbtn,
:root[data-sn-access="unavailable"] body #navbtn,
:root[data-sn-access="pending"] body #attention_btn,
:root[data-sn-access="locked"] body #attention_btn,
:root[data-sn-access="unavailable"] body #attention_btn,
:root[data-sn-access="pending"] body #nav_home,
:root[data-sn-access="pending"] body #nav_vault,
:root[data-sn-access="locked"] body #nav_home,
:root[data-sn-access="locked"] body #nav_vault,
:root[data-sn-access="unavailable"] body #nav_home,
:root[data-sn-access="unavailable"] body #nav_vault{display:none!important}
/* portal final H-10: SIGNED OUT, every portal document wears the header /account wears signed out — the brand, the
   language and the page's own sign-in card. The rail, the Sections button, the bell, search and the rail's other doors
   are the product's navigation; a visitor without a session gets a sign-in card, not six doors that each lead back to it. */
:root[data-sn-access="anonymous"] body .pnav,
:root[data-sn-access="anonymous"] body #navbtn,
:root[data-sn-access="anonymous"] body #attention_btn,
:root[data-sn-access="anonymous"] body #search_btn,
:root[data-sn-access="anonymous"] body #nav_vault{display:none!important}
:root[data-nav="side"][data-sn-access="anonymous"] body .wrap,
:root[data-nav="side"][data-sn-access="anonymous"] body footer{margin-inline-start:0}
:root[data-sn-access="locked"] body .wrap,
:root[data-sn-access="unavailable"] body .wrap{margin-inline-start:0!important;max-width:100%;padding:40px 24px 72px}
:root[data-sn-access="locked"] body footer,
:root[data-sn-access="unavailable"] body footer{margin-inline-start:0!important}
/* portal final ACCESS-1: #main is a direct child of <body> on every portal document (no .wrap), so the card sat flush
   under the header with no gap. The gap is the card's own now. The action row holds an <a class="btn"> beside
   <button>s; a page rule turns anchors into display:block, which put the link's label at the top of its box while
   the buttons centred theirs. The id-scoped rule keeps every action the same flex box, label centred. */
.sn-access-lock{display:block!important;visibility:visible;max-width:720px;margin:40px auto 0;padding:30px;text-align:center}
#sn_access_lock .sn-access-actions .btn{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-height:40px}
.sn-access-lock h1{margin:6px 0 8px;font-size:var(--fs-6);line-height:1.25}
/* §15.12 — no emoji anywhere in the product. shell.js now puts a stroked glyph here in
   currentColor; the box keeps its name and its lane so nothing that positions it has to move. */
.sn-access-icon{display:flex;justify-content:center;color:var(--mut);line-height:1}
.sn-access-icon svg{display:block;width:28px;height:28px}
.sn-access-lead{max-width:590px;margin:0 auto;color:var(--ink2);font-size:var(--fs-4)}
.sn-access-actions{display:flex;justify-content:center;gap:10px;flex-wrap:wrap;margin-top:22px}
.sn-access-legal{margin-top:28px;padding-top:20px;border-top:1px solid var(--line);text-align:start}
.sn-access-legal h2{margin:0 0 5px;font-size:var(--fs-5)}
.sn-access-legal p{margin:0}
.sn-access-legal-links{display:flex;gap:18px;flex-wrap:wrap;margin-top:12px}
.sn-access-lock .msg{max-width:560px;margin:14px auto 0;text-align:start}
/* The plan-boundary card a loop session sees where a plane outside its free proof answered 402
   (shell.js buildPlanBoundary). Quiet on purpose: information, not an alarm, and never a Retry. */
.sn-plan-boundary{display:block;border:1px dashed var(--line2);border-radius:0;background:var(--tile);padding:12px 16px;margin:0 0 12px}
.sn-plan-boundary-t{font-weight:600;font-size:var(--fs-4);color:var(--ink)}
.sn-plan-boundary-lead{margin:4px 0 0;color:var(--ink2);font-size:var(--fs-3);line-height:1.45}
.sn-plan-boundary-a{display:flex;gap:18px;flex-wrap:wrap;margin-top:10px}
.sn-plan-boundary-a a{font-weight:500;font-size:var(--fs-3);color:var(--accentTx);text-decoration:none}
.sn-plan-boundary-a a:hover{text-decoration:underline}
/* The wind-down banner (shell.js buildWindDownBanner, P0-10): a subscription that ended states its
   boundary ABOVE the workbench, never instead of it. Same quiet register as the card above — this is
   information, not an alarm, and nothing here failed — with a warm left rule so it reads as a state
   of the account rather than one more section of the page. */
.sn-winddown{display:block;border:1px solid var(--line2);border-inline-start:3px solid var(--warn);border-radius:0;background:var(--tile);padding:12px 16px;margin:0 0 12px}
.sn-winddown-t{font-weight:600;font-size:var(--fs-4);color:var(--ink)}
.sn-winddown-lead{margin:4px 0 0;color:var(--ink2);font-size:var(--fs-3);line-height:1.45}
.sn-winddown-a{display:flex;gap:18px;flex-wrap:wrap;margin-top:10px}
.sn-winddown-a a{font-weight:500;font-size:var(--fs-3);color:var(--accentTx);text-decoration:none}
.sn-winddown-a a:hover{text-decoration:underline}

/* ── controls ──────────────────────────────────────────────────────────────────────────────── */
/* SPEC-v3 §5 — HIERARCHY BY WEIGHT, NOT BY HUE (SPEC-v2 §5.6's principle, v3's values). Two buttons
   that do opposite things differ by LEVEL, never by colour at the same weight.
     primary    (ONE per surface)  600, --accent fill, --onaccent ink, 32px
     secondary                     500, transparent, 1px --line2, --ink
     tertiary / destructive        500, text only — underline on hover and focus, never at rest
   Sentence case always. 32px desktop clears WCAG 2.5.8's 24x24; a coarse pointer gets 36px, and the
   header's own controls stay at the pinned 44px box. A secondary button's hover is a --tile wash on
   a --line3 edge, not a blue one: blue means "yours to act on", and hovering is not that. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:32px;padding:0 12px;border:1px solid transparent;border-radius:var(--r-ctl);background:var(--btnbg);color:var(--onaccent);font:600 var(--fs-4)/1 inherit;cursor:pointer;text-decoration:none}
.btn:hover{filter:brightness(1.06)}
.btn:disabled,.btn[aria-disabled=true]{opacity:.55;cursor:default;filter:none}
.btn.ghost,.btn.sec2{background:transparent;border:1px solid var(--line2);color:var(--ink);font-weight:500;filter:none}
.btn.ghost:hover,.btn.sec2:hover{border-color:var(--line3);color:var(--ink);background:var(--tile)}
.btn.danger{background:transparent;border:0;padding:0 2px;color:var(--badTx);font:500 var(--fs-4)/1 inherit;text-decoration:none;filter:none;min-height:24px}
.btn.danger:hover,.btn.danger:focus-visible{filter:none;background:transparent;text-decoration:underline}
.btn.sm2{min-height:28px;padding:0 10px;font-size:var(--fs-4)}
@media(pointer:coarse){.btn{min-height:36px}.btn.sm2,.btn.danger{min-height:36px}}
/* §5.1 rule 3 — a control boundary is INFORMATION under WCAG 1.4.11, not decoration, which is why
   --line2 clears 3:1 against both grounds in both themes. */
input,select,textarea{font:inherit;font-size:var(--fs-4);color:var(--ink);background:var(--field);border:1px solid var(--line2);border-radius:var(--r-ctl);padding:5px 8px;min-height:28px;max-width:100%}
@media(pointer:coarse){input,select,textarea{min-height:36px}}
/* A checkbox is not a text field: without this the shared input rule forced #pp_disc's pinned
   18x18 box to a 28px (previously 44px) row height. */
input[type=checkbox],input[type=radio]{min-height:0;padding:0;border:0;accent-color:var(--accent)}
textarea{min-height:88px}
/* ⛔ appearance:auto HANDS THE CONTROL BACK TO THE OPERATING SYSTEM, AND THE COMPUTED STYLE LIES
   ABOUT IT. Measured before this pass: every <select> and every <input type=date> in the portal
   computed background rgb(10,19,34), border 1px rgb(69,106,176) and border-radius 3px in the dark
   theme — and Windows painted a WHITE box with black digits and its own rounded chevron anyway
   (OBSERVED, five of them across one /account/links filter row, plus the two in the top bar and
   the two on History). A screenshot is the only thing that catches this; getComputedStyle says the
   design is applied. appearance:none is the whole fix. Everything below is what the browser stops
   drawing for us and the product now has to draw for itself.

   The chevron is a background-image and not a mask, because a mask would take the select's own
   text with it — and a data: URI is a separate document, so it cannot read currentColor. That is
   why the two theme inks are written out literally here, once each, as --sn-caret. They are --mut's
   value in each theme and they are the only two colour literals in this file outside the palette
   blocks. Declared on `body`, never in a new :root block: portalshell §1 brace-matches the four
   :root blocks out of this file and out of server.js and compares them byte for byte. */
body{--sn-caret:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6' fill='none' stroke='%2355668a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.3 5 4.7 9 1.3'/%3E%3C/svg%3E")}
:root[data-theme="dark"] body{--sn-caret:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6' fill='none' stroke='%23a3b4d6' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.3 5 4.7 9 1.3'/%3E%3C/svg%3E")}
select{-webkit-appearance:none;appearance:none;cursor:pointer;background-image:var(--sn-caret);background-repeat:no-repeat;background-position:right 9px center;background-size:10px 6px;padding-inline-end:26px;text-overflow:ellipsis}
/* background-position has no logical form, so Arabic and Urdu need the one direction-scoped rule
   in the file. The chevron itself is vertical and therefore correct in both directions. */
[dir="rtl"] select{background-position:left 9px center}
/* A list box is not a drop-down: it has no closed state to point at. */
select[multiple],select[size]{background-image:none;padding-inline-end:8px;cursor:default}
/* The date fields keep the browser's own parsing and keyboard behaviour; only the paint changes.
   The picker button becomes the same masked-glyph idiom the rail uses (background:currentColor
   under a mask), so it follows the field's ink in both themes instead of staying a black OS icon
   on a dark ground. */
input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:none;appearance:none;background-color:var(--field);color:var(--ink)}
input[type=date]::-webkit-calendar-picker-indicator,input[type=time]::-webkit-calendar-picker-indicator,input[type=datetime-local]::-webkit-calendar-picker-indicator,input[type=month]::-webkit-calendar-picker-indicator{width:14px;height:14px;padding:0;margin:0;margin-inline-start:6px;cursor:pointer;opacity:.9;background:currentColor;-webkit-mask:var(--sn-cal) center/contain no-repeat;mask:var(--sn-cal) center/contain no-repeat;--sn-cal:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.4' y='5.2' width='17.2' height='15.4' rx='2'/%3E%3Cpath d='M3.4 10h17.2M8.2 3v4M15.8 3v4'/%3E%3C/svg%3E")}
input[type=date]::-webkit-datetime-edit,input[type=time]::-webkit-datetime-edit,input[type=datetime-local]::-webkit-datetime-edit,input[type=month]::-webkit-datetime-edit{color:var(--ink)}
input[type=date]::-webkit-datetime-edit-text,input[type=date]::-webkit-datetime-edit-day-field,input[type=date]::-webkit-datetime-edit-month-field,input[type=date]::-webkit-datetime-edit-year-field{color:inherit}
/* The file field was the last Windows control left in the product: a light-grey "Choose File"
   button with dark ink, unchanged on the dark theme (OBSERVED on /account → Profile → Company
   profile). ::file-selector-button styles that button in place, which keeps the input keyboard-
   operable, keeps its <label for> accessible name, and keeps the browser's OWN translation of the
   button and of "no file chosen" — all three of which a styled label over a visually-hidden input
   would have had to re-implement, the last of them eleven times. The two spellings are separate
   rules on purpose: one invalid selector inside a list drops the whole rule. */
input[type=file]{cursor:pointer;padding:4px;font-size:var(--fs-3);color:var(--ink);background:var(--field);border:1px solid var(--line2);border-radius:var(--r-ctl);min-height:36px}
input[type=file]::-webkit-file-upload-button{margin-inline-end:10px;padding:0 12px;min-height:26px;border:1px solid var(--line2);border-radius:var(--r-ctl);background:transparent;color:var(--ink);font:600 var(--fs-3)/1 var(--sans);cursor:pointer}
input[type=file]::file-selector-button{margin-inline-end:10px;padding:0 12px;min-height:26px;border:1px solid var(--line2);border-radius:var(--r-ctl);background:transparent;color:var(--ink);font:600 var(--fs-3)/1 var(--sans);cursor:pointer}
input[type=file]::-webkit-file-upload-button:hover{border-color:var(--accent);color:var(--accentTx)}
input[type=file]::file-selector-button:hover{border-color:var(--accent);color:var(--accentTx)}
label{display:block;font-size:var(--fs-3);font-weight:500;color:var(--ink2);margin:10px 0 4px}
.msg{display:none;margin-top:12px;padding:8px 12px;border-radius:0;font-size:var(--fs-3)}
.msg.ok{display:block;background:var(--okbg);border:1px solid var(--okbd);color:var(--okTx)}
.msg.bad{display:block;background:var(--badbg);border:1px solid var(--badbd);color:var(--badTx)}
.msg.warn{display:block;background:var(--warnbg);border:1px solid var(--warnbd);color:var(--warn)}

/* ── advanced disclosure: technical truth stays available, never in the consumer's face ────── */
details.adv{margin-top:10px;border-top:1px solid var(--line);padding-top:8px}
details.adv>summary{cursor:pointer;font:500 var(--fs-4)/1.4 var(--sans);color:var(--ink2);min-height:28px;display:flex;align-items:center}
details.adv code,code.mono{font:var(--fs-3)/1.5 var(--mono);font-variant-numeric:tabular-nums slashed-zero;unicode-bidi:isolate;background:var(--tile);border:1px solid var(--line);border-radius:var(--r-ctl);padding:1px 6px;overflow-wrap:anywhere}

/* ── drawers (full-screen on mobile) ───────────────────────────────────────────────────────── */
.drawer{position:fixed;inset:0;z-index:60;display:none}
.drawer.open{display:block}
.drawer .scrim{position:absolute;inset:0;background:var(--scrim)}
/* Logical insets: the drawer entered from the physical right in Arabic and Urdu too, which put it
   over the content it was describing instead of beside it. It floats and is dismissable, so --sh
   is correct here (§5.4). */
.drawer .panel{position:absolute;inset-block:0;inset-inline-end:0;width:min(560px,100%);background:var(--card);border-inline-start:1px solid var(--line2);box-shadow:var(--sh);overflow:auto;padding:16px 16px calc(24px + env(safe-area-inset-bottom))}
.drawer .dhead{display:flex;align-items:flex-start;gap:12px;margin-bottom:8px}
.drawer .dhead h2{margin:0;font:600 var(--fs-5)/1.3 var(--sans);flex:1}

/* ── shared account attention inbox ───────────────────────────────────────────────────────── */
.sn-attn-drawer .panel{width:min(480px,100%)}
.sn-attn-drawer .dhead{align-items:center;margin-bottom:12px}
/* §5.1 — the three summary figures were three boxed cards inside a panel that is already a box.
   Grouping is a rule and a shared baseline now: one --line3 rule under the block, hairlines between
   the columns, the micro-label idiom on the labels and the figures in the mono face. */
.sn-attn-summary{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0;margin:0 0 16px;border-block-end:1px solid var(--line3)}
.sn-attn-summary-card{display:flex;flex-direction:column;min-height:64px;padding:8px 12px;border:0;border-inline-start:1px solid var(--line);border-radius:0;background:transparent;color:var(--ink);text-decoration:none}
.sn-attn-summary-card:first-child{border-inline-start:0;padding-inline-start:0}
/* navscale follow-up (2026-09-16): the tile holds one link PER FIGURE, so the wash is per figure too —
   it shows which list the pointer is about to open. WCAG 2.2 AA 2.5.8: the sub rows are 24px targets. */
.sn-attn-summary-main,.sn-attn-summary-sub{color:inherit;text-decoration:none}
.sn-attn-summary-main{display:flex;flex-direction:column;flex:1}
.sn-attn-summary-main:hover,.sn-attn-summary-sub:hover{background:var(--tint)}
/* SPEC-v3 §1 — the labels read in sentence case at 12/500 and the figures in the product's own
   face with tabular numerals; mono is for hashes only. */
.sn-attn-summary-label{color:var(--mut);font:500 var(--fs-3)/1.3 var(--sans)}
.sn-attn-summary-value{display:flex;align-items:baseline;gap:4px;margin-top:6px;font-size:var(--fs-3);color:var(--mut)}
.sn-attn-summary-value strong{color:var(--ink);font:600 var(--fs-6)/1 var(--sans);font-variant-numeric:tabular-nums}
.sn-attn-summary-sub{display:flex;align-items:baseline;justify-content:space-between;gap:6px;min-height:24px;padding-top:6px;color:var(--mut);font-size:var(--fs-3);line-height:1.25}
.sn-attn-summary-main+.sn-attn-summary-sub{margin-top:auto}
.sn-attn-summary-sub strong{color:var(--ink2);font-size:var(--fs-3);font-weight:600;font-variant-numeric:tabular-nums}
.sn-attn-tools{display:flex;align-items:center;gap:12px;margin-bottom:4px}
.sn-attn-tools h3{flex:1;margin:0;font:600 var(--fs-5)/1.4 var(--sans);color:var(--ink)}
.sn-attn-empty{margin:24px 0;color:var(--mut);font-size:var(--fs-3);text-align:center}
.sn-attn-empty[hidden]{display:none}
/* The inbox is a register: one row height, one rule between rows, no radius, no fill at rest. */
.sn-attn-list{list-style:none;margin:0;padding:0;border-top:1px solid var(--line3)}
.sn-attn-list li{position:relative;border-bottom:1px solid var(--line)}
/* §15.13 — the unread mark is never the only signal: the title also goes to 700. A square, because
   a circle would be a third radius doing no work. */
.sn-attn-list li.unread::before{content:"";position:absolute;inset-inline-start:0;top:14px;width:6px;height:6px;background:var(--accent)}
.sn-attn-event{display:flex;flex-direction:column;gap:2px;min-height:44px;padding-block:8px;padding-inline:14px 8px;color:var(--ink);text-decoration:none;border-radius:0}
.sn-attn-event:hover{background:var(--tint)}
.sn-attn-event-title{font-size:var(--fs-4);font-weight:400}
.sn-attn-list li.unread .sn-attn-event-title{font-weight:600}
/* portal final H-03: the row's second line names WHICH job, guest or case — one line, ellipsised, in the body ink */
.sn-attn-event-sub{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink2);font-size:var(--fs-3)}
.sn-attn-event-time{color:var(--mut);font-size:var(--fs-3);font-variant-numeric:tabular-nums;unicode-bidi:isolate}
.sn-attn-toast{position:fixed;z-index:80;inset-block-start:72px;inset-inline-end:16px;width:min(360px,calc(100vw - 32px));opacity:0;transform:translateY(-8px);pointer-events:none;transition:opacity .16s ease,transform .16s ease}
.sn-attn-toast.show{opacity:1;transform:none;pointer-events:auto}
/* A toast floats and can be dismissed — one of the four subjects allowed --sh and --r-float. */
.sn-attn-toast-action{display:flex;align-items:center;gap:10px;width:100%;min-height:44px;padding:8px 12px;border:1px solid var(--line2);border-radius:var(--r-float);background:var(--card);color:var(--ink);box-shadow:var(--sh);font:600 var(--fs-4)/1.35 inherit;text-align:start;cursor:pointer}
.sn-attn-toast-action:hover{border-color:var(--accent)}
.sn-attn-toast-icon{position:relative;width:16px;height:16px;flex:none;border-radius:var(--r-ctl);background:var(--tint)}
.sn-attn-toast-icon::after{content:"";position:absolute;inset:4px;border-radius:0;background:var(--accent)}

/* ── tables ────────────────────────────────────────────────────────────────────────────────── */
/* SPEC-v2 §5.7 — a table is the DEFAULT answer to a list: a sticky micro-label head with the one
   --line3 rule under it, cells separated by --line, numbers right-aligned and tabular. */
.tblwrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
table.tbl{width:100%;border-collapse:collapse;font-size:var(--fs-4)}
table.tbl th{text-align:start;font:500 var(--fs-3)/1.4 var(--sans);color:var(--mut);padding:6px 8px;border-bottom:1px solid var(--line3);white-space:nowrap}
table.tbl th:first-child,table.tbl td:first-child{padding-inline-start:0}
table.tbl th:last-child,table.tbl td:last-child{padding-inline-end:0}
table.tbl td{padding:6px 8px;border-bottom:1px solid var(--line);vertical-align:middle}
table.tbl td.num,table.tbl th.num{text-align:end;font-variant-numeric:tabular-nums}
table.tbl tbody tr:last-child td{border-bottom:0}
table.tbl tbody tr:hover{background:var(--tint)}

/* ── states ────────────────────────────────────────────────────────────────────────────────── */
.empty{border:1px dashed var(--line2);border-radius:0;padding:24px 20px;text-align:center}
.empty h3{margin:0 0 6px;font-size:var(--fs-5)}
.empty p{margin:0 auto 12px;max-width:52ch;color:var(--ink2);font-size:var(--fs-3)}
/* §5.5 — this is the ONE gradient left in the stylesheet, and it is motion on a loading placeholder
   between two tokenised stops rather than decoration. The crest keeps its own (§1.1). */
.sk{background:linear-gradient(90deg,var(--tile) 0%,var(--card2) 50%,var(--tile) 100%);background-size:200% 100%;animation:skm 1.1s linear infinite;border-radius:var(--r-ctl);display:inline-block}
.sk-line{height:14px;width:120px}
@keyframes skm{to{background-position:-200% 0}}

/* ── responsive: 720px is the shell breakpoint the spec pins ───────────────────────────────── */
@media(max-width:720px){
  .top{padding:0 12px;gap:8px}
  .top .pill{display:none}
  .top>nav{display:none!important}
  .top>.skinpick{display:none}
  /* ⛔ THE HEADER ROW MUST FIT. Measured on a 375px phone: brand + @handle + Sections + language +
     theme came to 438px, so the page scrolled sideways — the overflow gate failing on the narrowest
     screen, which is the one this shell exists for. The handle goes first: it is decoration here and
     the customer's own account page names them. What stays is the brand, the way to every
     destination, and the theme control. */
  .top #who{display:none}
  /* Notifications remain available in the Sections sheet. Keeping the full-size desktop control in
     this row makes the shell wider than a real phone in several languages.
     RE-PINNED 2026-09-11 (SPEC-v3 §5, portalshell §4): the sign-out button left this list. It is no
     longer a control IN THE ROW — it lives inside the account menu, a position:absolute panel that
     cannot widen the header, so hiding it here would only take it out of the menu on the one screen
     where the menu is the nearest way to it. The Sections sheet keeps its own Sign out as well. */
  .top #attention_btn{display:none!important}
  /* navscale WP2 (DESIGN §D.1): the Search button leaves the phone row for the bell's reason — one more 44px box on a
     375px bar — and the Sections sheet carries a Search row instead (shell.js buildSheet). An id rule outranks every
     class rule that gives .iconbtn a display, so it needs no !important. */
  .top #search_btn{display:none}
  .pnav{display:none}
  #navbtn{display:inline-flex}
  .wrap{padding:18px 14px 64px}
  .card{padding:14px}
  .drawer .panel{width:100%;border-inline-start:0}
  .sn-attn-summary{grid-template-columns:1fr}
  .sn-attn-summary-card{min-height:0;padding-inline:0;border-inline-start:0;border-block-start:1px solid var(--line)}
  .sn-attn-summary-card:first-child{border-block-start:0}
  .shell-sheet-tools{display:grid;gap:10px;margin:10px 6px 4px;padding-top:12px;border-top:1px solid var(--line)}
  /* portal final H-11: Cloud Vault is a destination like the section links above it, so its label starts on their
     line — the tools block's 6px inset is for the buttons, and the link steps back out of it. */
  .shell-sheet-tools>.shell-sheet-vault{margin-inline:-6px}
  .shell-sheet-skinpick{display:flex;height:auto;min-height:52px;margin:0;padding:4px 12px;border:0;color:var(--ink)}
  .shell-sheet-skinpick .shell-sheet-label{font-size:var(--fs-4);font-weight:600;white-space:nowrap}
  .shell-sheet-skinpick select{flex:1;max-width:none;margin-inline-start:auto}
  .shell-sheet-action{width:100%}
  .grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .big{font-size:var(--fs-5)}
  :root[data-sn-access="locked"] body .wrap,
  :root[data-sn-access="unavailable"] body .wrap{padding:24px 14px 64px}
  .sn-access-lock{margin:16px 12px 0;padding:22px 16px}
  .sn-access-actions{align-items:stretch;flex-direction:column}
  .sn-access-actions .btn{width:100%}
}
/* Below 400px every pixel in the header row is contested. Measured on a 375px phone after hiding
   the handle: brand 112 + Sections 83 + language 97 + theme 44 + gaps + padding still came to 392
   against 375. Tightening the gutters and the language control brings it inside with room to spare
   — and the language NAME still reads, which is what the control is for. */
@media(max-width:400px){
  .top{padding:0 8px;gap:6px}
  /* ⛔ THE PHONE MUST STILL SAY WHO THIS IS. `font-size:0` collapsed BOTH words to nothing —
     MEASURED on a 393px Pixel: .brand was 20x20, and the "State" text node and the "Null" span
     were each 0px wide, so the phone showed a bare crest and no company name anywhere on the
     screen. A crest alone can be a deliberate lockup; this was a collapse, and it read as one.
     The lockup SCALES instead: the same crest, the same two words, the same 800 identity weight,
     at --fs-4, with the brand's 8px gap scaled to 6px so the proportions of the mark hold. It fits
     now because the appearance preference has left this row for the Sections sheet (below). */
  .top .brand{font-size:var(--fs-4);gap:6px}
  .top .brand .mk,.top .brand>svg{width:18px;height:18px}
  .langpick select{max-width:88px;padding-inline:6px 22px;background-position:right 6px center}
  [dir="rtl"] .langpick select{background-position:left 6px center}
}
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
}

/* ── v3: the six-item nav becomes a LEFT RAIL ──────────────────────────────────────────────────
   THIS LIVES HERE BECAUSE THE NAV LIVES HERE. shell.js builds the same nav.pnav element for
   /account/jobs, /account/links and /account/agents, and those are separate PAGES, not routes of
   the /account SPA. While the rail was declared only in server.js's inline stylesheet, exactly one
   of the four surfaces had it: picking "Sidebar (v3)" gave a sidebar on Home and the old horizontal
   strip everywhere else, so the control named a layout three quarters of the portal did not have.

   FIXED POSITIONING, NOT A GRID. /account and /account/jobs carry the nav as a direct child of
   <body>, but /account/links and /account/agents mount the whole chrome into <div id="shellhost"> —
   a body-level grid cannot place a nested element, and those two pages also keep six or more
   drawers at body level that auto-placement would scatter into cells. Fixed positioning does not
   care where in the tree the element sits, which is the only property that holds for all four.

   Logical properties throughout (inset-inline-start, margin-inline-start, border-inline-end), so
   Arabic and Urdu mirror the rail with no second rule.

   ⛔ NEVER DECLARE `display` ON .pnav IN THIS BLOCK. The ≤720px rule above hides the rail in favour
   of the Sections sheet, and an attribute selector out-scores a bare class no matter the source
   order or the media query — a display declaration here would silently un-hide the mobile nav. */
/* /account still carries its older page-local header and v2 frame declarations after this shared
   stylesheet. The `body` compound below is deliberate: it makes the shared v3 geometry outrank
   those later declarations, so Home, Proof Requests, Shared Links and AI Agents keep the exact same
   60px header, control heights, background and content origin during document navigation. */
/* ⭐ portal polish (2026-09-29) — ONE CANVAS FOR EVERY PORTAL PAGE: THE CARD'S OWN GROUND. The owner saw Home and Jobs as
   white and Videos, Shared Links, Calendar and Account as tinted. MEASURED: all six painted --bg (#f4f7fd); Home read white
   because its one card fills the view, and Jobs IS white because its register paints --card to the window's foot and
   edge (#q_fill). The pages that read tinted were the ones whose content does not cover the ground. So the canvas is the
   register's ground, --card, on every page — the rail and the top bar already are — and every card and table keeps its
   hairline edge (Stripe's, Linear's and Attio's white canvas with ruled surfaces). Dark follows the same token. */
:root[data-nav="side"] body{background:var(--card)}
:root[data-nav="side"] body .top{
  position:sticky;top:0;z-index:30;display:flex;align-items:center;flex-wrap:nowrap;
  gap:12px;row-gap:normal;height:60px;min-height:60px;padding:0 24px;
  background:var(--card);border-bottom:1px solid var(--line2);
}
:root[data-nav="side"] body .brand{
  gap:8px;font:800 17px/1 var(--sans);
}
:root[data-nav="side"] body .brand .mk,
:root[data-nav="side"] body .brand>svg{width:18px;height:18px;flex:none}
/* The context label, de-boxed and given the region hairline instead — same reasoning as the base
   rule above, repeated here because /account's own stylesheet is later in source order. */
:root[data-nav="side"] body .pill{
  display:inline-flex;align-items:center;min-height:24px;
  font:500 var(--fs-3)/1 var(--sans);letter-spacing:0;text-transform:none;
  color:var(--mut);border:0;border-radius:0;padding:0;background:transparent;
  border-inline-start:1px solid var(--line);padding-inline-start:12px;
}
:root[data-nav="side"] body .tgl,
:root[data-nav="side"] body .iconbtn{
  width:44px;height:44px;min-width:44px;padding:0;border:1px solid var(--line2);
  background:var(--card);color:var(--mut);border-radius:var(--r-ctl);
}
/* SPEC-v3 §5 — the header controls that stay on the bar keep the 44px box above (pinned) and are
   drawn as quiet hit areas: transparent until hover, like the base rule. Repeated at this weight
   because the pinned v3 rule declares the border and the fill at (0,3,1). */
:root[data-nav="side"] body .top .iconbtn{border-color:transparent;background:transparent}
:root[data-nav="side"] body .top .iconbtn:hover{background:var(--tile);color:var(--ink)}
/* THE PAGE'S ONE PRIMARY ACTION JOINS THE BAR under v3, above 720px (on a phone shell.js puts it back
   in its page head: the bar has no room for it in German or French). adoptPrimary MOVES the page's own
   element (same id, same listener, same gates), so it can never offer a create flow the page has
   refused. It shows only on an ENTITLED document — pending, locked, unavailable and signed-out
   documents have no create flow to offer — and the element's own inline display (jobs.html hides it
   on a spent free proof) still wins over this rule. 32px, 36px under a coarse pointer (§5). */
:root[data-nav="side"] body .top>.sn-primary{display:none}
:root[data-nav="side"][data-sn-access="entitled"] body .top>.sn-primary{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;flex:none;
  width:auto;height:32px;min-height:32px;margin:0;padding-block:0;padding-inline:10px 12px;
  white-space:nowrap;text-align:center;
}
:root[data-nav="side"] body .top>.sn-primary svg{display:block;flex:none}
/* …and wherever it stands (back in its page head on a phone), the + and the words share one centre
   line. /account's inline sheet re-declares .btn as inline-block later in source order, which sat the
   SVG on the text baseline, a few pixels above the words (SEEN at 393). (0,1,1) out-ranks that .btn
   without an !important; the element's own inline display (a spent free proof) still wins. */
body .sn-primary{display:inline-flex;align-items:center;justify-content:center;gap:6px}
body .sn-primary[hidden]{display:none}
@media(pointer:coarse){
  :root[data-nav="side"][data-sn-access="entitled"] body .top>.sn-primary{height:36px;min-height:36px}
}
:root[data-nav="side"] body .langpick{
  position:relative;display:inline-flex;align-items:center;gap:0;height:44px;
  padding:0;border:0;background:transparent;
}
:root[data-nav="side"] body .langpick>svg{display:none}
:root[data-nav="side"] body .langpick-icon,
:root[data-nav="side"] body .skinpick-icon{display:none}
/* ⛔ appearance:auto WAS HERE, AND IT IS WHY THE TWO CONTROLS BESIDE THE WORDMARK WERE DRAWN BY
   WINDOWS ON EVERY PAGE OF THE PORTAL. It also has to be background-COLOR and not the `background`
   shorthand: the shorthand resets background-image, and the chevron this control now needs lives
   there (see the --sn-caret block in ── controls ──). The 44px box is a pinned literal. */
:root[data-nav="side"] body .langpick select{
  -webkit-appearance:none;appearance:none;
  height:44px;min-height:44px;max-width:9.5em;padding-inline:10px 26px;
  background-color:var(--card);color:var(--mut);border:1px solid var(--line2);border-radius:var(--r-ctl);
  background-image:var(--sn-caret);background-repeat:no-repeat;background-position:right 9px center;background-size:10px 6px;
  font-size:var(--fs-4);font-weight:400;
}
:root[data-nav="side"][dir="rtl"] body .langpick select{background-position:left 9px center}

:root[data-nav="side"] body .pnav{
  position:fixed;
  inset-block-start:60px;                 /* the sticky header's own height */
  inset-block-end:0;
  inset-inline-start:0;
  width:248px;box-sizing:border-box;
  z-index:20;                             /* below .top (30), which stays full width above the rail */
  flex-direction:column;align-items:stretch;gap:0;
  padding:12px 0;
  border-bottom:0;border-inline-end:1px solid var(--line);
  background:var(--card);
  overflow-y:auto;overscroll-behavior:contain;
}
/* Attribute-guarded: without the guard a page whose nav carries no title still renders the ::before
   box, i.e. a strip of empty padding above the first link. The rail title, the rail items and the
   page's own text all begin on the SAME --gut edge (§4.2) — alignment is the cheapest thing in this
   design and the most visible when it is missing. */
/* SPEC-v3 §5 — the section label reads "Workspace" at 12/500 in the muted ink, sentence case, the way
   Linear labels "Workspace" and "Favorites". */
:root[data-nav="side"] body .pnav[data-railtitle]::before{
  content:attr(data-railtitle);display:block;
  padding:4px var(--gut) 6px;font:500 var(--fs-3)/1.4 var(--sans);color:var(--mut);
}
/* SPEC-v3 §5 — a rail item is 32px of quiet 13/400 reading text with a 16px stroked glyph. Linear's
   pattern: the item is an INSET row with the control radius, so its wash sits 8px inside the rail
   while the text stays on the page's one --gut text edge. The active item is the --tint wash, ink
   and 500 — the 2px blue inline-start rule is gone (neither reference uses one), and
   aria-current="page" still says it to a screen reader. Logical properties, so Arabic and Urdu
   mirror it with no second rule. */
:root[data-nav="side"] body .pnav a{
  border-bottom:0;border-radius:var(--r-ctl);border-inline-start:0;
  margin-inline:8px;padding-inline:calc(var(--gut) - 8px);text-align:start;
  display:flex;align-items:center;gap:10px;min-height:32px;box-sizing:border-box;
  font-size:var(--fs-4);font-weight:400;color:var(--ink2);
}
:root[data-nav="side"] body .pnav a:hover{background:var(--tile);color:var(--ink)}
:root[data-nav="side"] body .pnav a.on{background:var(--tint);color:var(--ink);font-weight:500;box-shadow:none}
/* Inward, so a focused rail item never overlaps its neighbours' edges (§5.8). */
:root[data-nav="side"] body .pnav a:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}

/* Content clears the rail.
   SPEC-v2 §4.2/§13.1 — THE ONE BYTE-EXACT LITERAL THIS REDESIGN MOVES, and portalshell.test.js §5b
   is re-pinned to the new string with the reason beside it. The frame is full bleed to the rail
   edge because a register wants to reach it: the 28/32px inset was chrome, and chrome is exactly
   what the density work spends. Every surface re-declares the text edge on its own blocks with
   --gut (24px), so nothing loses its alignment — it gains ONE alignment across four pages, instead
   of a page inset here and a card padding there. /account/jobs already shipped this locally in the
   register step; that page-local override is now redundant and stays only as a defensive repeat. */
:root[data-nav="side"] body .wrap{margin-inline-start:248px;max-width:none;padding:0 0 56px}
:root[data-nav="side"] body footer{margin-inline-start:248px}
/* data-rail="off" is set by the signed-out portal, which hides the nav. Without it the sign-in card
   and the footer keep a 248px band of empty background on their start side. Higher specificity than
   the two rules above so it wins wherever it is set. */
:root[data-nav="side"][data-rail="off"] body .wrap,
:root[data-nav="side"][data-rail="off"] body footer{margin-inline-start:0}

/* Below 1180px v3 falls back to the shipped horizontal strip — the layout classic and v2 already use
   and that is already proven at 768 and 375. RESET inset/width/height, never max-height: the rail
   gets its height from inset-block, and clearing a property this rule does not set leaves a
   full-viewport-tall box with all content one screen below the fold.

   ⭐ 1024 → 1180, 2026-09-08, AND IT IS THE ROOT FIX OF THE WHOLE RESPONSIVE SURVEY. The rail is a
   FIXED 248px, so the window and the content box are two different numbers, and every page's
   breakpoints were written against the window: at a 1023px window the content was 1023px, and at
   1024 it was 776 — one pixel of extra window cost 247px of content AND asked the page to draw its
   widest column set. MEASURED on the shipped build: the queue's REQUEST cell was 286.9px on a 393px
   phone and 31.5px at 1024 and at 1112, i.e. one letter of each job's name, nineteen rows deep,
   while CUSTOMER kept its full 160px beside it. A quarter of a 1024px window was navigation.
   1180 is not a round number picked for tidiness — it is this product's own long-standing content
   measure (the classic frame is max-width:1180px), and it is the width at which 248px of rail stops
   being a fifth of the window. Above it the content box is never below 932px; below it the strip
   costs zero horizontal pixels and the page gets the whole window. That makes the ladder monotone —
   a wider window is now always more content — which is what lets every page below express its
   breakpoints as one number instead of two.
   Nothing is lost between 1024 and 1179: the strip carries the same six destinations, keeps its own
   "you are here" rule, and is the layout classic and v2 have always shipped. */
@media(max-width:1179px){
  /* ⛔ NO overflow-x:auto. A horizontally scrolling nav strip is the thing the Sections sheet
     replaced — destinations past the fold were invisible, which is why the ≤720px rule hides the
     strip entirely. Falling back to the SHIPPED horizontal nav means falling back to its overflow
     behaviour too, not inventing a scroller v2 and classic do not have. */
  :root[data-nav="side"] body .pnav{
    position:static;inset:auto;width:auto;height:auto;
    flex-direction:row;border-inline-end:0;border-bottom:1px solid var(--line);
    overflow:visible;padding:10px 16px 0;gap:4px;background:transparent;
  }
  /* The rail's inline-start rule is a vertical idiom; in the horizontal strip the same "you are
     here" signal goes back under the item, so reset it rather than leaving a stray 2px notch. */
  :root[data-nav="side"] body .pnav a{
    border-inline-start:0;border-bottom:2px solid transparent;border-radius:var(--r-ctl);
    margin-inline:0;padding-inline:12px;min-height:36px;
  }
  :root[data-nav="side"] body .pnav a.on{border-inline-start-color:transparent;border-bottom-color:var(--accent)}
  :root[data-nav="side"] body .pnav[data-railtitle]::before{display:none}
  /* Full bleed at every width, so a page has ONE text edge to declare instead of one per breakpoint
     (the tablet fallback used to add 20px of its own and every child inset landed on top of it). */
  :root[data-nav="side"] body .wrap{margin-inline-start:0;max-width:none;padding:0 0 56px}
  :root[data-nav="side"] body footer{margin-inline-start:0}
  /* ⭐ THE HEADER ROW MUST FIT HERE TOO, AND IT DID NOT (survey D2). The phone sheds the context
     label and the @handle at ≤720 with a measured reason; between 721 and 1179 the same row kept
     brand + CUSTOMER PORTAL + Cloud Vault + appearance + theme + language + @handle + Sign out +
     the bell and simply overflowed. MEASURED on the shipped build at 768: documentElement.scrollWidth
     − clientWidth = +107px on /account#home, #records and #account and +59 on /account/agents, with
     button#out — the ONLY sign-out — rendered 107px outside the viewport. Same page at 834: +41.
     So the band sheds exactly what the phone sheds first, and for the same reason: the pill is a
     label the strip below it already carries, and the handle is decoration the Account page owns.
     ⛔ EVERYTHING THAT IS A DESTINATION OR A CONTROL STAYS — Cloud Vault, the appearance picker,
     theme, language, notifications and Sign out. #navbtn is hidden above 720, so the Sections sheet
     is NOT a fallback here: anything hidden in this band is unreachable, which is why the picker
     (the skin escape hatch) is not in this list. Removing the pill and the handle buys 254px against
     a worst case of 154px at 721px — measured in the widest of the eleven languages, not in English.
     The v3 compounds are repeated because :root[data-nav="side"] body .pill and … .langpick declare
     display at (0,3,1) and a bare .top .pill would silently lose to them (the same trap the ⛔ note
     on .pnav describes). */
  .top .pill{display:none}
  .top #who{display:none}
  :root[data-nav="side"] body .top .pill{display:none}
  :root[data-nav="side"] body .top #who{display:none}
}
@media(max-width:720px){
  :root[data-nav="side"] body .top{gap:8px;padding:0 12px}
  :root[data-nav="side"] body .wrap{padding:0 0 48px}
  /* ⛔ THE ≤720px RULE `.top>.skinpick{display:none}` HAS BEEN DEAD SINCE THE RAIL SHIPPED. Its
     comment says the header row must fit on a 375px phone and names the appearance picker as the
     first thing to go — but `:root[data-nav="side"] body .langpick` sets display:inline-flex at
     (0,3,1) and out-scores a bare `.top>.skinpick` at (0,2,0) no matter the source order, exactly
     the trap the ⛔ note on .pnav warns about in this same block. So on v3 — the DEFAULT skin — a
     preference control set once in a lifetime was the widest thing in every phone header, wider
     than the company's own name. This restores the intent at matching specificity. Appearance is
     still reachable, and portalshell §6 asserts it: #sheet_skinsel inside the Sections sheet. */
  :root[data-nav="side"] body .top>.skinpick{display:none}
  /* Same trap, same fix: giving .pill a display value above (it needs inline-flex to sit the region
     hairline at a readable 24px) would have out-scored the shipped .top .pill{display:none} and put
     the context label back into the phone row it was removed from. MEASURED: it cost 84px of a
     393px header. The rail and the Sections sheet both name the surface; the header does not. */
  :root[data-nav="side"] body .top .pill{display:none}
}
@media(max-width:400px){
  :root[data-nav="side"] body .top{gap:6px;padding:0 8px}
  :root[data-nav="side"] body .top .brand{gap:6px;font-size:var(--fs-4)}
  :root[data-nav="side"] body .top .brand .mk,
  :root[data-nav="side"] body .top .brand>svg{width:18px;height:18px}
  :root[data-nav="side"] body .langpick select{max-width:88px;padding-inline:6px 22px;background-position:right 6px center}
  :root[data-nav="side"][dir="rtl"] body .langpick select{background-position:left 6px center}
}

/* ── v3 rail glyphs (2026-08-31 onboarding polish) ────────────────────────────────────────────
   New customers read shape before words, so every destination gets an icon. href-keyed on purpose:
   the /account page carries its nav in markup while jobs/links/agents build theirs in shell.js, and
   the SAME attribute selectors reach both without touching shell.js's test-stubbed DOM builder.
   Icons are alpha masks painted with currentColor, so they follow the link's own colour in every
   skin and theme; the ::before only exists for hrefs listed below — an unknown destination simply
   renders no glyph instead of an unmasked square. Gated to the v3 rail like the rest of this file's
   sidebar rules; the anchors are already flex with a 10px gap (see the rail rule above). */
:root[data-nav="side"] body .pnav a[href="#home"]::before,:root[data-nav="side"] body .pnav a[href="/account"]::before,:root[data-nav="side"] body .pnav a[href$="#records"]::before,:root[data-nav="side"] body .pnav a[href="/account/jobs"]::before,:root[data-nav="side"] body .pnav a[href="/account/calendar"]::before,
:root[data-nav="side"] body .pnav a[href="/account/videos"]::before,:root[data-nav="side"] body .pnav a[href="/account/links"]::before,:root[data-nav="side"] body .pnav a[href="/account/agents"]::before,:root[data-nav="side"] body .pnav a[href$="#account"]::before{content:"";flex:0 0 auto;width:16px;height:16px;background:currentColor;opacity:.72;-webkit-mask:var(--ni) center/contain no-repeat;mask:var(--ni) center/contain no-repeat}
:root[data-nav="side"] body .pnav a.on::before{opacity:1}
:root[data-nav="side"] body .pnav a[href="#home"],:root[data-nav="side"] body .pnav a[href="/account"]{--ni:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 10.6 12 3.4l8.5 7.2'/%3E%3Cpath d='M5.8 9.7V20h4.4v-5.4h3.6V20h4.4V9.7'/%3E%3C/svg%3E")}
:root[data-nav="side"] body .pnav a[href$="#records"]{--ni:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m12 3.2 8.8 4.9L12 13 3.2 8.1z'/%3E%3Cpath d='m3.2 12.4 8.8 4.9 8.8-4.9'/%3E%3Cpath d='m3.2 16.6 8.8 4.9 8.8-4.9'/%3E%3C/svg%3E")}
:root[data-nav="side"] body .pnav a[href="/account/jobs"]{--ni:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.2 3.6h5.6v2.8H9.2z'/%3E%3Cpath d='M15 4.8h2.2a1.8 1.8 0 0 1 1.8 1.8v12a1.8 1.8 0 0 1-1.8 1.8H6.8A1.8 1.8 0 0 1 5 18.6v-12a1.8 1.8 0 0 1 1.8-1.8H9'/%3E%3Cpath d='m9 13.4 2.2 2.2 4-4.6'/%3E%3C/svg%3E")}
/* Calendar (2026-09-22): a month sheet with two rings — the one glyph nobody mistakes for anything else. */
:root[data-nav="side"] body .pnav a[href="/account/calendar"]{--ni:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.6' y='5.4' width='16.8' height='15' rx='2'/%3E%3Cpath d='M3.6 10.2h16.8'/%3E%3Cpath d='M8.4 3.6v3.6'/%3E%3Cpath d='M15.6 3.6v3.6'/%3E%3C/svg%3E")}
:root[data-nav="side"] body .pnav a[href="/account/videos"]{--ni:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.6' y='5.6' width='16.8' height='12.8' rx='2'/%3E%3Cpath d='m10.4 9.4 4.2 2.6-4.2 2.6z'/%3E%3C/svg%3E")}
:root[data-nav="side"] body .pnav a[href="/account/links"]{--ni:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.6 13.4a4.6 4.6 0 0 0 6.5 0l2.9-2.9a4.6 4.6 0 1 0-6.5-6.5l-1.7 1.7'/%3E%3Cpath d='M13.4 10.6a4.6 4.6 0 0 0-6.5 0L4 13.5A4.6 4.6 0 1 0 10.5 20l1.7-1.7'/%3E%3C/svg%3E")}
:root[data-nav="side"] body .pnav a[href="/account/agents"]{--ni:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M11.5 3.8 13.3 8.5 18 10.3l-4.7 1.8-1.8 4.7-1.8-4.7L5 10.3l4.7-1.8z'/%3E%3Cpath d='m18.6 15.3.8 2.1 2.1.8-2.1.8-.8 2.1-.8-2.1-2.1-.8 2.1-.8z'/%3E%3C/svg%3E")}
:root[data-nav="side"] body .pnav a[href$="#account"]{--ni:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='8' r='3.9'/%3E%3Cpath d='M4.6 20.4c1.5-3.7 4.4-5.5 7.4-5.5s5.9 1.8 7.4 5.5'/%3E%3C/svg%3E")}

/* ── BIDI: A LATIN RUN INSIDE AN RTL ROW (SPEC-v2 §12.3, 2026-09-08) ──────────────────────────
   The CSS half of jobs.html's bidi() — read its comment for the defect (every Latin job title in
   the Urdu and Arabic register ellipsised from its FIRST word) and for why dir="auto" fixes it.
   This rule is the other half and it is NOT optional: dir="auto" also re-resolves text-align:start,
   so a Latin cell would align to the LEFT of its column while its own column head and its Arabic
   neighbours stayed on the right — one column hanging off two edges. match-parent resolves
   start/end against the PARENT's direction (the row's), so the column keeps ONE text edge in every
   language while each cell keeps its own truncation end. Ungated: reading the end of a title
   instead of its beginning is a defect under the classic skin too.
   ⛔ BOTH SPELLINGS, IN THIS ORDER, AND NEITHER IS REDUNDANT. Blink shipped this for years as
   -webkit-match-parent; a browser that knows only the old name drops the second declaration as
   invalid, one that knows the standard name takes it because it is later. OBSERVED: with the
   unprefixed keyword alone the whole rule parsed to an EMPTY BLOCK in the review harness's
   Chromium and every RTL column silently kept the wrong text edge. */
[dir="auto"]{text-align:-webkit-match-parent;text-align:match-parent}

/* ── v4 CRITIC FIXES (2026-09-11) ─────────────────────────────────────────────────────────────── */
/* A11Y-12 (WCAG 1.4.3): placeholder text took Chrome's default #757575 — 4.14:1 on the search well, and
   the queue's search label is visually hidden, so the placeholder IS its visible name. The muted ink
   measures 4.9:1 on --tile and 5.4:1 on --field light, 8.5:1 dark. */
::placeholder{color:var(--mut);opacity:1}
/* OE-26 — the account menu's switches read as switches: a pill track with a round knob (the one other
   round shape SPEC-v3 allows beside an avatar, where the pattern needs it); role=switch is unchanged.
   The two value controls share one width, so their edges line up down the menu. */
#acct_menu .acct-sw{border-radius:12px}
#acct_menu .acct-knob{border-radius:50%}
#acct_menu .langpick select{width:124px;max-width:none}
/* OE-22 — a locked-plan card is a finished state, not a wireframe: a solid --tint wash, no dashed edge. */
.sn-plan-boundary{border:0;background:var(--tint)}
/* OE-19 — the drawer's chrome is quiet: Close is a transparent 44px hit area, Mark all read is text. */
.sn-attn-drawer .dhead .iconbtn,:root[data-nav="side"] body .sn-attn-drawer .dhead .iconbtn{border-color:transparent;background:transparent}
.sn-attn-drawer .dhead .iconbtn:hover,:root[data-nav="side"] body .sn-attn-drawer .dhead .iconbtn:hover{border-color:transparent;background:var(--tile);color:var(--ink)}
.sn-attn-tools .btn{min-height:28px;padding:0 4px;border-color:transparent;background:transparent;color:var(--ink2);font-weight:500}
.sn-attn-tools .btn:hover,.sn-attn-tools .btn:focus-visible{background:transparent;color:var(--ink);text-decoration:underline}

/* ── SPEC-v4 (2026-09-12): THE BRAND ICON TILE, THE FIGURE CARD AND THE ICON CARD ───────────────
   The owner, signed in on the live Templates page: "too much text — use icons, visualise; respect the
   brand layout and the corporate icon." The brand's own component (marketing .acard .ic, rendered in
   refs/statenull-02.png) is a soft brand-tint square holding the icon, and its figures and choices
   are short cards. These shared parts carry that language onto every portal surface:
     .ictile  the tile: 40px on cards and heads, .is-s = 32px in dense rows. What it holds is CONTENT
              (a template's own em, a lifecycle or section emoji, or a category glyph) and it is
              always aria-hidden: the card's title carries the name. Never in a button label, a
              status or a menu (SPEC-v4 §1).
     .fig     a figure card: tile, the number large, a 12px label, an optional .fbar for a ratio.
     .icard   an empty state: tile and one sentence (and the page's one action, when it has one).
     .sdots   a step count as dots (filled = the count, eight slots), .gtag the one small tag.
   ⛔ OFF IN CLASSIC. A tile is new markup, and the classic skin must keep rendering what it rendered,
   so it is display:none there and only the v2 gate draws it. --ictile is a palette token in BOTH
   themes (the four blocks above, byte-identical with PORTAL_PAGE_HTML). */
.ictile{display:none}
/* v6a critic (TPL-9): EXACTLY the marketing tile (.acard .ic): 44px, 12px corners, a 22px glyph and a 1px
   --line2 edge — kept flat, because the crest stays the only gradient. At 40px with no edge, the grey
   glyphs (🔧 📰 🔗) nearly vanished on the flat lilac in light. */
:root[data-skin="v2"] .ictile{display:inline-grid;place-items:center;flex:0 0 auto;box-sizing:border-box;width:44px;height:44px;
  border:1px solid var(--line2);border-radius:12px;background:var(--ictile);color:var(--accent);font-size:22px;line-height:1;font-weight:400;
  font-style:normal;text-align:center;overflow:hidden;user-select:none;-webkit-user-select:none}
:root[data-skin="v2"] .ictile.is-s{width:32px;height:32px;border-radius:8px;font-size:16px}
/* SPEC-v5-home (2026-09-15): a lane head's tile takes its colour-law hue — the lane that is yours in brand blue, the
   waiting lane uncoloured — on Jobs and on Home alike (jobs.html keeps identical copies for now). */
:root[data-skin="v2"] .ictile.g-review{color:var(--accent)}
:root[data-skin="v2"] .ictile.is-g{color:var(--ink2)}
:root[data-skin="v2"] .fig{display:grid;grid-template-columns:44px minmax(0,1fr);grid-template-areas:"ic v" "ic l";
  column-gap:12px;align-items:center;min-width:0;padding:14px 16px;border:1px solid var(--line);border-radius:12px;
  background:var(--card);box-shadow:none;color:var(--ink)}
:root[data-skin="v2"] .fig > .ictile{grid-area:ic;align-self:start}
:root[data-skin="v2"] .icard{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:16px;
  border:1px solid var(--line);border-radius:12px;background:var(--card);box-shadow:none}
/* The thin ratio bar: a --tile track, the part in --mut (neutral) unless its figure owns a hue in the
   colour law (yours = blue, overdue = red, done = green) — the figure's own class says which. */
:root[data-skin="v2"] .fbar{display:block;height:4px;margin-top:8px;border-radius:2px;background:var(--tile);overflow:hidden}
:root[data-skin="v2"] .fbar > i{display:block;height:100%;border-radius:2px;background:var(--mut)}
/* Step dots: the filled count in the middle ink, the empty slots up to eight in the hairline weight. */
:root[data-skin="v2"] .sdots{display:inline-flex;align-items:center;gap:3px;flex:0 0 auto}
:root[data-skin="v2"] .sdots > i{display:block;width:6px;height:6px;border-radius:50%;background:var(--line)}
:root[data-skin="v2"] .sdots > i.on{background:var(--ink2)}
/* v6a critic (BELL-1) — each notification row leads with the glyph of what happened; the panel head states
   the unread count the bell's badge shows. */
.sn-attn-event-glyph{display:none}
:root[data-skin="v2"] .sn-attn-event{display:grid;grid-template-columns:16px minmax(0,1fr);column-gap:10px;row-gap:2px;align-content:center}
:root[data-skin="v2"] .sn-attn-event > .sn-attn-event-glyph{grid-row:1 / span 2;align-self:start;margin-top:2px}
:root[data-skin="v2"] .sn-attn-event > .sn-attn-event-title,:root[data-skin="v2"] .sn-attn-event > .sn-attn-event-time,:root[data-skin="v2"] .sn-attn-event > .sn-attn-event-sub{grid-column:2}
:root[data-skin="v2"] .sn-attn-event.has-sub > .sn-attn-event-glyph{grid-row:1 / span 3}
:root[data-skin="v2"] .sn-attn-event-glyph{display:inline-flex;flex:0 0 auto;color:var(--ink2)}
:root[data-skin="v2"] .sn-attn-event-glyph svg{display:block}
:root[data-skin="v2"] .sn-attn-unread{font-weight:400;color:var(--mut)}
/* v6a critic (FREE-1) — the plan boundary is an ICON CARD (SPEC-v4 §2): the page's tile, the title, one short
   line, the two ways on — not a band of prose over a feature the account cannot open. */
:root[data-skin="v2"] .sn-plan-boundary{display:grid;grid-template-columns:44px minmax(0,1fr);grid-template-areas:"ic t" "ic l" "ic a";column-gap:14px;row-gap:2px;
  align-items:center;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--card)}
:root[data-skin="v2"] .sn-plan-boundary > .ictile{grid-area:ic;align-self:start}
:root[data-skin="v2"] .sn-plan-boundary-t{grid-area:t}
:root[data-skin="v2"] .sn-plan-boundary-lead{grid-area:l;margin:0}
:root[data-skin="v2"] .sn-plan-boundary-a{grid-area:a;margin-top:6px}
:root[data-skin="v2"] .gtag{display:inline-block;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;padding:1px 8px;border-radius:6px;background:var(--tile);color:var(--ink2);
  font-size:var(--fs-2);font-weight:500;line-height:1.6}
/* ── v6e (2026-09-14): ONE FIGURE ROW for every surface — Home, Records and Shared Links. The owner, signed in
   live at 1900px: the three Shared Links figure cards sat 220px apart. That row was the shell's auto-FIT grid,
   which collapses the empty tracks and stretches three cards to a third of the window each, and the card's own
   320px cap then left the rest of each track as ground between them. A figure row is auto-FILL: ~270px cards
   (220px minimum, growing to fill their track), a 14px gap, left-aligned — the row's leftover stays at its
   end, exactly as Home's row already did. The two values are custom properties so a row that has to carry an
   id-level rule of its own (Home's #v3_kpis, Records' grid — both outrank a class) consumes the same numbers
   through var() instead of restating them; the class is the idiom, the ids only point at it. */
/* ⭐ SPEC-v5-home §4 (2026-09-15): ONE RULED STRIP. The cards left the figure row: at 1900 an auto-fill row of three
   cards ended at 1059 in a 1589px box, half of the row empty ground. A figure row is now ONE container spanning its
   parent, divided into EQUAL cells by hairlines, each cell's figure group starting at its inner start (so stacked
   cells line their tiles up). auto-FIT is right again: inside one ruled band there is no gap and no per-cell cap, so a
   wide cell is part of the band, while auto-fill would leave empty tracks inside it. The hairlines need no per-cell
   rule: every cell draws its inline-start and block-start line, and the -1px margins push the first column's and the
   first row's lines under the container's clipped edge, for any wrap and in both writing directions.
   .figrow.figrow is (0,4,0) on purpose: /account loads this file BEFORE its inline sheet, whose generic v2 .card rules
   are (0,3,0) and later, so a single class would lose the cell chrome to them. The strip clips, so the focus ring is
   drawn INSIDE the cell. Records and Shared Links keep their strip above the table; Home puts it beside or after the
   lanes. The two custom properties stay, so an id that must restate the columns (Home's, Records') points at them. */
:root[data-skin="v2"] .figrow{--figmin:160px;--figcols:repeat(auto-fit,minmax(var(--figmin),1fr));--figgap:0px;display:grid;grid-template-columns:var(--figcols);gap:var(--figgap);border:1px solid var(--line);border-radius:12px;background:var(--card);overflow:hidden}
:root[data-skin="v2"] .figrow.figrow > *{min-width:0;margin:0;margin-inline-start:-1px;margin-block-start:-1px;padding:14px 16px;border:0;border-inline-start:1px solid var(--line);border-block-start:1px solid var(--line);border-radius:0;background:transparent;box-shadow:none;display:grid;grid-template-columns:44px auto;grid-template-areas:"ic v" "ic l";column-gap:12px;row-gap:2px;align-items:center;justify-content:start}
:root[data-skin="v2"] .figrow.figrow > * > .ictile{grid-area:ic;align-self:start}
:root[data-skin="v2"] .figrow.figrow > :focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
@media(max-width:720px){
  :root[data-skin="v2"] .figrow{--figmin:140px}
  :root[data-skin="v2"] .figrow.figrow > :nth-child(odd):last-child{grid-column:1/-1}
}

/* ═══ "More options" — the ONE disclosure per form (WAVE1-SPEC §2 S, rules a–g) ═══════════════════════════════════
   A real <button aria-expanded> whose accessible name is "More options" followed by the facts it holds (rule g), so a
   screen-reader user hears "More options, Link works 14 days, Notify: @leo" before deciding to open it. The facts are
   inline list items, each one complete translatable sentence isolated in its own <bdi> (rule a) — the separator is
   drawn here, never concatenated into the words. The body is [hidden] until it is opened; the restated display:none
   out-ranks a .wizgrid's display:grid under the dense skin. 24px targets at least (WCAG 2.2 2.5.8).
   ⚠️ It sits HERE, before the navscale WP2 block, not inside WP2..door or after the PORTAL POLISH block at the end of
   the file: those regions are held to "no !important" (shellpager.test.js, shellpolish.test.js — the latter also to
   "every restyle of existing markup gated on v2"), and the [hidden] rule below needs its !important while the Profile
   card's radios are not a v2-only restyle. */
.snmore{margin:14px 0 4px;grid-column:1/-1}
.snmore-btn{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;width:100%;min-height:36px;margin:0;padding:6px 8px;border:1px solid var(--line);border-radius:var(--r-ctl);background:transparent;color:var(--ink);font:600 var(--fs-4)/1.35 var(--sans);text-align:start;cursor:pointer}
.snmore-btn:hover{background:var(--tile)}
.snmore-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.snmore-l::before{content:"";display:inline-block;width:0;height:0;margin-inline-end:8px;border-block:5px solid transparent;border-inline-start:6px solid currentColor;vertical-align:1px;transition:transform .12s ease}
[dir="rtl"] .snmore-l::before{transform:scaleX(-1)}
.snmore-btn[aria-expanded="true"] .snmore-l::before{transform:rotate(90deg)}
[dir="rtl"] .snmore-btn[aria-expanded="true"] .snmore-l::before{transform:scaleX(-1) rotate(90deg)}
.snmore-sum{flex:1 1 240px;min-width:0;font-weight:400;font-size:var(--fs-3);color:var(--mut)}
.snmore-sum[hidden]{display:none}
.snmore-fact{display:inline}
.snmore-fact+.snmore-fact::before{content:" \00B7  "}
.snmore-body{margin-top:6px}
.snmore-body[hidden]{display:none!important}
@media(pointer:coarse){.snmore-btn{min-height:44px}}
/* The /account › Profile card (SNShell.uiModeCard): two radios, one per line, the words a click target. */
.sn-uimode-grp{display:grid;gap:6px;margin:8px 0 4px}
.sn-uimode-opt{display:flex;align-items:flex-start;gap:10px;min-height:24px;cursor:pointer;font-weight:400}
.sn-uimode-opt input{width:auto;margin:3px 0 0}
/* ── navscale WP2 (2026-09-15, DESIGN §C.2 + §D): ONE PAGER AND THE PORTAL SEARCH ─────────────────────────────
   Ungated shell chrome, like .sheet and .drawer: both appear in every skin, and every value below is a token the
   four palette blocks already define — no new custom property, no colour literal, no font-family, no !important,
   no animation. */
/* The pager (shell.js SNShell.pager): words, not chevrons. The range in the muted 12px ("11–20 of 1,234", an
   isolated LTR run inside an Arabic or Urdu sentence — shell.js wraps it in U+2066…U+2069), Previous / Next as the
   28px secondary buttons (.btn.ghost.sm2, SPEC-v3 §3) and the rows-per-page select. `hidden` is restated because
   .btn and this row declare a display, and an author display beats the UA's [hidden]. */
.sn-pager{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:0;padding:0;color:var(--ink2);font-size:var(--fs-4)}
.sn-pager[hidden],.sn-pager [hidden]{display:none}
.sn-pager-range{color:var(--mut);font-size:var(--fs-3);white-space:nowrap;unicode-bidi:isolate;font-variant-numeric:tabular-nums}
.sn-pager-btns{display:inline-flex;align-items:center;gap:6px}
.sn-pager .btn{color:var(--ink2)}
label.sn-pager-size{display:inline-flex;align-items:center;margin:0;color:var(--ink2);font-size:var(--fs-4);font-weight:400}
.sn-pager-size select{height:28px;min-height:28px;padding-block:0;font-size:var(--fs-4)}
@media(pointer:coarse){.sn-pager .btn,.sn-pager-size select{height:auto;min-height:44px}}

/* The bar's Search button: the quiet 44px .iconbtn the bell is (.top .iconbtn), first right of the slack. Below 720px
   it leaves the row like the bell (the 720 block) and the Sections sheet carries a Search row instead. */
.sn-search-btn svg{display:block}
/* Only an ENTITLED document searches: a pending, locked, unavailable or signed-out document has nothing the search
   may read, so neither copy is offered (shell.js refuses Ctrl/⌘+K and "/" there as well). */
:root:not([data-sn-access="entitled"]) #search_btn,:root:not([data-sn-access="entitled"]) .shell-sheet-find{display:none}
/* ⭐ navscale follow-up §2 (DESIGN §E.3: "44px top-bar search button, 44px sheet button"). WP2 moved the bar's
   44px Search control into this sheet, where .btn's coarse-pointer floor (the 36px rule above) made it 36 —
   MEASURED 36px at 393 on a touch context, 8px under the size the spec names for it. The floor is raised for the
   GROUP, not for the Search row alone: the three sheet actions are one stack of equal boxes (Search / Notifications
   / Sign out, measured 36/36/36 between a 52px Cloud Vault row and a 52px appearance row), and a single 44px box
   between two 36px ones is a bump the eye reads as a mistake. Nothing else in the sheet moves: the 52px rows keep
   their height, the 10px gaps are untouched, and a fine pointer sees no change at all. */
@media(pointer:coarse){.shell-sheet-action{min-height:44px}}

/* The search dialog (#sn_find): a scrim and a panel top-anchored at 12vh, min(640px, 100vw − 32px) wide — the one
   floating idiom the shell already has (--card, a --line2 edge, --r-float, --sh). Head: the visible title and a
   quiet close; the field; the one line that says what is never searched; then the grouped results in the queue's
   row grammar (16px glyph, 13/500 title, 12px meta, the status glyph and word at the end); a key legend at the foot.
   The active option — the combobox's focus, which never leaves the field — is the --tint wash AND the portal's own
   inset focus ring (the rail's, §5.8): a wash alone is ~1.1:1 on --card and cannot be a focus indicator. */
.sn-find{position:fixed;inset:0;z-index:85;display:none}
.sn-find.open{display:block}
.sn-find .scrim{position:absolute;inset:0;background:var(--scrim)}
.sn-find-panel{position:absolute;inset-block-start:12vh;inset-inline:0;margin-inline:auto;display:flex;flex-direction:column;width:min(640px,calc(100vw - 32px));max-height:min(640px,76vh);overflow:hidden;background:var(--card);color:var(--ink);border:1px solid var(--line2);border-radius:var(--r-float);box-shadow:var(--sh);font:400 var(--fs-4)/1.4 var(--sans);text-align:start}
.sn-find-head{display:flex;align-items:center;gap:8px;padding-block:6px 0;padding-inline:16px 6px}
.sn-find-title{flex:1;min-width:0;margin:0;color:var(--mut);font:500 var(--fs-3)/1.4 var(--sans)}
.sn-find-x{display:inline-flex;align-items:center;justify-content:center;flex:none;width:36px;height:36px;margin:0;padding:0;border:0;border-radius:var(--r-ctl);background:transparent;color:var(--mut);cursor:pointer}
.sn-find-x:hover{background:var(--tile);color:var(--ink)}
.sn-find-x svg{display:block}
.sn-find-field{position:relative;margin-block:2px 0;margin-inline:16px}
.sn-find-gl{position:absolute;inset-block:0;inset-inline-start:12px;display:flex;align-items:center;color:var(--mut);pointer-events:none}
.sn-find-gl svg{display:block}
input.sn-find-q{display:block;width:100%;max-width:none;height:44px;min-height:44px;margin:0;padding-block:0;padding-inline:40px 12px;border:1px solid var(--line2);border-radius:var(--r-ctl);background:var(--field);color:var(--ink);font:400 var(--fs-5)/1.3 var(--sans);-webkit-appearance:none;appearance:none}
input.sn-find-q::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}
.sn-find-hint{margin-block:8px 10px;margin-inline:16px;color:var(--mut);font-size:var(--fs-3);line-height:1.45}
.sn-find-body{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain;padding-block:4px 8px;border-top:1px solid var(--line3)}
.sn-find-msg{margin-block:10px 4px;margin-inline:16px;color:var(--ink2);font-size:var(--fs-4)}
.sn-find-msg[hidden],.sn-find-notes[hidden]{display:none}
.sn-find-list[aria-busy="true"]{opacity:.6}
.sn-find-grp{padding-block:4px 2px}
.sn-find-gh{display:flex;align-items:baseline;gap:6px;padding-block:6px 4px;padding-inline:16px;color:var(--mut);font:500 var(--fs-3)/1.4 var(--sans)}
.sn-find-gn{font-variant-numeric:tabular-nums}
.sn-find-opt{display:grid;grid-template-columns:16px minmax(0,1fr) auto;align-items:center;column-gap:10px;min-height:44px;margin-inline:6px;padding-block:5px;padding-inline:10px;border-radius:var(--r-ctl);color:var(--ink);text-decoration:none;cursor:pointer}
.sn-find-opt:hover{background:var(--tile)}
.sn-find-opt.on{background:var(--tint);outline:2px solid var(--accent);outline-offset:-2px}
.sn-find-og{display:inline-flex;color:var(--ink2)}
.sn-find-og svg{display:block}
[dir="rtl"] .sn-find-og.is-dir svg{transform:scaleX(-1)}
.sn-find-ot{display:flex;flex-direction:column;min-width:0}
.sn-find-o1{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink);font-size:var(--fs-4);font-weight:500}
.sn-find-o2{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mut);font-size:var(--fs-3)}
.sn-find-os{display:inline-flex;align-items:center;gap:6px;color:var(--mut);font-size:var(--fs-3);white-space:nowrap}
.sn-find-os svg{display:block;flex:none}
.sn-find-os.is-done svg{color:var(--okTx)}
.sn-find-ck{stroke:var(--card)}
.sn-find-all .sn-find-o1{color:var(--accentTx)}
.sn-find-notes{margin-block:6px 0;margin-inline:16px;color:var(--mut);font-size:var(--fs-3)}
.sn-find-notes p{margin:4px 0}
.sn-find-foot{display:flex;flex-wrap:wrap;align-items:center;gap:6px 14px;padding-block:8px;padding-inline:16px;border-top:1px solid var(--line);color:var(--mut);font-size:var(--fs-3)}
.sn-find-foot kbd{display:inline-block;min-width:18px;margin-inline-end:4px;padding-inline:4px;border:1px solid var(--line2);border-radius:var(--r-ctl);color:var(--ink2);font-size:var(--fs-3);line-height:16px;text-align:center}
@media(max-width:720px){
  .sn-find-panel{inset-block-start:8px;width:calc(100vw - 32px);max-height:calc(100vh - 16px);max-height:calc(100dvh - 16px)}
}
@media(pointer:coarse){
  .sn-find-foot{display:none}
  .sn-find-x{width:44px;height:44px}
  .sn-find-opt{min-height:48px}
}
/*SN_COMMSDOOR_BEGIN*/
/* ── THE DOOR TO CHAT & CALLS (2026-09-21) ───────────────────────────────────────────────────────
   The seventh item of the rail, and the only one that LEAVES the portal (comms.statenull.com: another
   origin, another session). So it is drawn apart from the six: a hairline before it and an "opens
   elsewhere" arrow after its name — both decoration; shell.js says the same thing in words for a
   screen reader. It never takes the "you are here" style, because you never are. Everything between
   these two markers is stripped before hashing while SN_COMMS_ENABLED is off (server.js
   commsDoorBlocks), so the dark portal keeps the stylesheet bytes — and the ?v= — it had. */
.sn-door{--nx:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7.5 16.5 16.5 7.5'/%3E%3Cpath d='M9.5 7.5h7v7'/%3E%3C/svg%3E")}
.pnav a.sn-door,.sheet a.sn-door{display:inline-flex;align-items:center}
.sheet a.sn-door{display:flex}
.pnav a.sn-door::after,.sheet a.sn-door::after,.sn-doorcard a.sn-door::after{content:"";flex:0 0 auto;width:12px;height:12px;margin-inline-start:6px;background:currentColor;opacity:.62;-webkit-mask:var(--nx) center/contain no-repeat;mask:var(--nx) center/contain no-repeat}
[dir="rtl"] a.sn-door::after{transform:scaleX(-1)}
.pnav .sn-door-sep{flex:0 0 auto;align-self:center;width:1px;height:16px;margin:0 6px 2px;background:var(--line2,var(--line))}
.sheet .sn-door-sep{display:block;height:1px;margin:6px 12px;background:var(--line)}
@media(max-width:860px){.pnav .sn-door-sep,.pnav a.sn-door::after{display:none}}
@media(min-width:1180px){
  :root[data-nav="side"] body .pnav .sn-door-sep{align-self:stretch;width:auto;height:1px;margin:8px calc(var(--gut)) 8px}
  :root[data-nav="side"] body .pnav a.sn-door{--ni:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.2 5.4h15.6v10.8h-9.4l-4.4 3.6v-3.6H4.2z'/%3E%3Cpath d='M8.4 9.4h7.2M8.4 12.4h4.6'/%3E%3C/svg%3E")}
  :root[data-nav="side"] body .pnav a.sn-door::before{content:"";flex:0 0 auto;width:16px;height:16px;background:currentColor;opacity:.72;-webkit-mask:var(--ni) center/contain no-repeat;mask:var(--ni) center/contain no-repeat}
  :root[data-nav="side"] body .pnav a.sn-door::after{margin-inline-start:auto}
}
/* The Home and Shared Links entries: one quiet card, one SECONDARY action (the page keeps its one
   filled primary), the sentence first because it says what the door is and what it cannot do yet. */
.sn-doorcard{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 16px}
.sn-doorcard .sn-doorcard-tx{flex:1 1 220px;min-width:0}
.sn-doorcard h3{margin:0 0 4px}
.sn-doorcard p{margin:0}
.sn-doorcard a.sn-door{flex:0 0 auto;display:inline-flex;align-items:center;min-height:36px}
/* ⛔ NO CROSSING AT PHONE WIDTH (door fix H2). 720px is the SAME line comms.html isPhone() draws: below it that page
   says "Continue in the app" and nothing else, signed in or not - so a door here would open onto a wall (and a plain
   click would mint a 12-hour comms session for a page that can do nothing). Every door and every desk-only sentence
   is taken out; the Home card keeps its title and says where chat and calls ARE on a phone. The Sections sheet
   carries no door at all (shell.js). !important because page stylesheets load after this file. */
.sn-door-phone{display:none}
@media(max-width:720px){
  .sn-doorcard a.sn-door,a.link.sn-door,.sn-door-desk{display:none!important}
  .sn-door-phone{display:block}
}
/* Door lows S5: the one sentence the door says when it does NOT open (a lapsed plan). A polite status line near the
   foot of the page, in the shell's own tokens, above nothing important and gone after a while (shell.js doorSay). */
.sn-door-note{position:fixed;inset-inline:16px;bottom:16px;z-index:60;max-width:560px;margin:0 auto;padding:10px 14px;border:1px solid var(--line);border-radius:10px;background:var(--card);color:var(--ink);box-shadow:0 4px 18px rgba(0,0,0,.14)}
.sn-door-note[hidden]{display:none}
/*SN_COMMSDOOR_END*/

/* ── PORTAL POLISH (2026-09-22, SPEC portal-polish §A) ──────────────────────────────────────────────────────────────
   The owner: "does the customer portal need a polish like Stripe or Paddle?" — yes, targeted. The structure (rail,
   tables, pager, lanes) was already at the reference level; what read as a settings dump was four things: cards with
   no surface, headings underlined with the heavy --line3 rule, two filled buttons in one view, and status told by a
   bare dot / usage told as text / storage drawn as an empty bar. MEASURED reference (not eyeballed): Paddle's
   customer-portal subscription page (developer.paddle.com subscription-page SVG) — card #f7f8f9, radius ~17px at
   1440, shadow 0 2px 4px rgba(0,0,0,.15); status pill 20px tall, radius 4, the word inside it; one outlined action.

   ⛔ HOW THIS BLOCK IS ALLOWED TO EXIST
   1. NEW TOKENS LIVE ON `body`, NEVER IN A :root BLOCK. portalshell §1 brace-matches the four :root blocks out of this
      file and out of PORTAL_PAGE_HTML and compares them byte for byte; the --sn-caret precedent above is the model.
      Every colour literal this block needs is inside the two token rules directly below — nowhere else.
   2. EVERY RESTYLE OF EXISTING MARKUP IS GATED ON THE v2 SKIN (the default), and most of it through
      `:where(:root[data-skin="v2"])`, i.e. at the SAME specificity as the base rule it replaces. That is on purpose:
      a page that deliberately flattens one of its own .card / .sec / table cells (Shared Links' KPI cells and its
      register card, the job record's ruled heads, the queue's flat chips) keeps winning with its own later rule.
      Only a property a page never overrides but /account's later inline v2 sheet re-declares at (0,2,0) — the
      section size, the body size, the control radius, the tertiary button — takes the `body` compound (0,2,1), the
      same idiom the rail uses above. No !important anywhere in this block.
   3. NEW COMPONENTS (.sn-pill, .sn-meter, .sn-card-h, .sn-mark/.sn-lane, .dt, .tr, .btn.tx) are UNGATED and drawn only
      in tokens, so the classic skin renders them legibly too — the one visible classic change is new markup that a
      page chooses to write, never a restyle of old markup.
   MEASURED contrast (WCAG 2.2, ink on the composited fill, both grounds, both themes — numbers in the commit):
   every pill pair >= 5.9:1 light and >= 6.6:1 dark; the meter track edge (--line2) >= 3.05:1 against --card and --bg;
   every meter fill >= 4.3:1 against its track. */
body{
  --sn-r-card:12px;
  --sn-sh-card:0 1px 2px rgba(12,20,36,.06),0 2px 6px rgba(12,20,36,.06);
  --sn-card-bd:var(--line);
  --sn-sp-1:4px;--sn-sp-2:8px;--sn-sp-3:12px;--sn-sp-4:16px;--sn-sp-5:24px;--sn-sp-6:32px;
  --sn-fs-title:20px;--sn-fs-section:15px;--sn-fs-body:14px;--sn-fs-meta:12px;
  --sn-pill-ok-bg:#dcf5e8;--sn-pill-ok-tx:#075a33;
  --sn-pill-info-bg:#e6edfc;--sn-pill-info-tx:#2a4fc4;
  --sn-pill-warn-bg:#fbefd6;--sn-pill-warn-tx:#7a4d06;
  --sn-pill-bad-bg:#fde4e4;--sn-pill-bad-tx:#a51818;
  --sn-pill-neu-bg:#eceff4;--sn-pill-neu-tx:#3d4a63;
  --sn-meter-track:var(--tile);--sn-meter-edge:var(--line2);--sn-meter-fill:var(--accent);
}
:root[data-theme="dark"] body{
  --sn-sh-card:0 1px 2px rgba(0,0,0,.35),0 2px 8px rgba(0,0,0,.25);
  --sn-pill-ok-bg:rgba(70,211,154,.16);--sn-pill-ok-tx:#6fe0b0;
  --sn-pill-info-bg:rgba(91,140,255,.18);--sn-pill-info-tx:#9dbbff;
  --sn-pill-warn-bg:rgba(255,194,75,.16);--sn-pill-warn-tx:#ffcf6e;
  --sn-pill-bad-bg:rgba(255,122,122,.16);--sn-pill-bad-tx:#ff9c9c;
  --sn-pill-neu-bg:rgba(163,180,214,.14);--sn-pill-neu-tx:#c3d0ea;
}

/* A.2 — type. Body reads at 14 (Stripe's body size; was 13), section heads and card titles at 15/600, the page title
   stays 20/600, meta stays 12. Weights stay 400/500/600. Only INHERITED text moves: anything a page sized explicitly
   with --fs-4 keeps its size, which is what keeps dense registers dense. */
:root[data-skin="v2"] body{font-size:var(--sn-fs-body)}
:where(:root[data-skin="v2"]) .card>h3:first-child{font-size:var(--sn-fs-section);line-height:1.35}

/* A.8 — a section head without the heavy rule; the card below it is the grouping. 24px above, 8px to its card. */
:where(:root[data-skin="v2"]) .sec{margin:24px 0 8px;padding-bottom:0;border-bottom:0;line-height:1.35}
:root[data-skin="v2"] body .sec{font-size:var(--sn-fs-section)}

/* A.4 — the card is a SURFACE: hairline edge, 12px corners, the soft two-layer shadow, 20px inside (16 on a phone),
   16px between stacked cards. Strips and rows stay flat — .figrow cells (0,4,0), table rows, .sn-attn-list, the rail
   — and things that float keep --sh/--r-float. */
:where(:root[data-skin="v2"]) .card{background:var(--card);border:1px solid var(--sn-card-bd);border-radius:var(--sn-r-card);box-shadow:var(--sn-sh-card);padding:20px}
:where(:root[data-skin="v2"]) .card+.card{margin-top:16px}
/* The figure cards (AI Agents, the queue's metric cards) already had the 12px corner; they gain the same surface. */
:root[data-skin="v2"] .fig{box-shadow:var(--sn-sh-card)}
/* AI Agents draws its four figure cards as `.tabpanel .grid>.card` (12px corners, --line edge, --card) with a flat
   box-shadow:none at (0,3,0) in its own later sheet; (0,4,0) here gives them the one card shadow and nothing else. */
:root[data-skin="v2"] .tabpanel .grid>.card{box-shadow:var(--sn-sh-card)}
/* Card header (Paddle's "Payments … View all"): the title at the start, at most one tertiary link at the end. A card
   whose body is a LIST draws one hairline under its head (.is-list); any other card draws none. */
.sn-card-h{display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:4px 12px;margin:0 0 12px}
.sn-card-h>h2,.sn-card-h>h3,.sn-card-h>.sn-card-t{flex:1 1 auto;min-width:0;margin:0;padding:0;border:0;font:600 var(--sn-fs-section)/1.35 var(--sans);color:var(--ink)}
.sn-card-h.is-list{padding-bottom:12px;border-bottom:1px solid var(--line)}

/* A.5 — ONE button system. Primary = the filled .btn (at most ONE per view); secondary = .btn.ghost / .btn.sec2;
   TERTIARY (new) = .btn.tx, text only in the link ink, underlined on hover and focus; destructive = .btn.danger,
   unchanged. Controls take 6px corners under v2 so they sit with 12px cards; the pinned :root --r-ctl stays 3px
   (the header's 44px boxes, the rail items and the menus keep it). */
:root[data-skin="v2"] body .btn{border-radius:6px}
:root[data-skin="v2"] body input:where(:not([type=checkbox]):not([type=radio])),:root[data-skin="v2"] body select,:root[data-skin="v2"] body textarea{border-radius:6px}
body .btn.tx{background:transparent;border-color:transparent;color:var(--accentTx);font-weight:500;padding:0 4px;filter:none;text-decoration:none}
body .btn.tx:hover,body .btn.tx:focus-visible{background:transparent;border-color:transparent;color:var(--accentTx);filter:none;text-decoration:underline}
/* THE ONE-PRIMARY MECHANISM (shell.js SNShell.pagePrimary). A view that shows its OWN primary ("Get my download link",
   "Let back in", "Open ticket") sets data-sn-page-primary on <html>, and the page's "New job" (.sn-primary — in the
   bar above 720px, back in its page head below) takes the SECONDARY look. Paint only: it stays in place, clickable,
   focusable, with its id, its listener and its gates — no control is hidden. (0,4,1) out-ranks every .btn fill in
   the page sheets. */
:root[data-sn-page-primary] body .sn-primary.btn{background:transparent;border-color:var(--line2);color:var(--ink);font-weight:500;filter:none}
:root[data-sn-page-primary] body .sn-primary.btn:hover{background:var(--tile);border-color:var(--line3);color:var(--ink);filter:none}

/* A.6 — STATUS PILLS replace bare dots. 20px, radius 4, the WORD carries the meaning and the fill is the second
   signal (WCAG 1.4.1). Optional 12px glyph before the word. The one semantic map (SPEC A.6):
     is-ok   done / live / paid        — link active; job approved, delivered; subscription active
     is-info yours / in progress       — job open, in progress, accepted, ready for review; trial
     is-warn attention                 — link paused; changes requested, hold; paused, cancellation scheduled
     is-bad  failed / overdue          — link revoked; declined, the overdue delta; payment overdue, suspended
     is-neu  neutral / ended           — expired, idle, closed, archived, ASSIGNED; canceled, not licensed, free proof
   Violet is never a status (it is the brand mark's colour alone). shell.js SNShell.pill(kind, text, glyph). */
.sn-pill{display:inline-flex;align-items:center;gap:4px;height:20px;max-width:100%;padding:0 8px;border:0;border-radius:4px;background:var(--sn-pill-neu-bg);color:var(--sn-pill-neu-tx);font:500 var(--sn-fs-meta)/1 var(--sans);letter-spacing:0;text-transform:none;white-space:nowrap;vertical-align:middle;overflow:hidden;text-overflow:ellipsis}
.sn-pill.is-ok{background:var(--sn-pill-ok-bg);color:var(--sn-pill-ok-tx)}
.sn-pill.is-info{background:var(--sn-pill-info-bg);color:var(--sn-pill-info-tx)}
.sn-pill.is-warn{background:var(--sn-pill-warn-bg);color:var(--sn-pill-warn-tx)}
.sn-pill.is-bad{background:var(--sn-pill-bad-bg);color:var(--sn-pill-bad-tx)}
.sn-pill.is-neu{background:var(--sn-pill-neu-bg);color:var(--sn-pill-neu-tx)}
.sn-pill>svg{display:block;width:12px;height:12px;flex:none}
.sn-pill::before,.sn-pill::after{content:none}
.sn-pill-t{overflow:hidden;text-overflow:ellipsis}

/* A.7 — THE METER. Label and value on one line (the value tabular, end-aligned, an isolated LTR run), a 6px bar,
   an optional meta line (reset date, hint). The EMPTY track must be visible on --card (the storage bar drew nothing at
   609 KB of 50 GB): so the track carries a 1px inset edge in --line2 — the control-boundary weight, 3.28:1 / 3.05:1
   light and 3.23:1 / 3.69:1 dark against --card / --bg (SPEC A.7 names --line, which measures 1.18:1 and fails its
   own G2 3:1 requirement; the requirement wins over the literal, as it did for --line2 itself). VISIBLE FLOOR: any
   use above zero draws at least 2px; zero draws the empty track (.is-empty). >= 80% fills --warn, >= 90% or over the
   limit fills --bad and the value turns --badTx. No limit known (.is-nolimit): the value alone, no bar.
   Markup: .sn-meter[role=meter] > .sn-meter-h(.sn-meter-l + .sn-meter-v) + .sn-meter-bar > i[style="--p:<pct>"]
   + .sn-meter-m — shell.js SNShell.meter({label, used, limit, fmt, meta, pct}) builds exactly this. */
.sn-meter{display:block;width:100%;max-width:560px;min-width:0}
.sn-meter-h{display:flex;align-items:baseline;justify-content:space-between;gap:12px;min-width:0}
.sn-meter-l{flex:1 1 auto;min-width:0;color:var(--ink);font-size:var(--sn-fs-body);font-weight:500;overflow-wrap:anywhere}
.sn-meter-v{flex:0 0 auto;color:var(--ink2);font-size:var(--sn-fs-meta);font-variant-numeric:tabular-nums;white-space:nowrap;unicode-bidi:isolate;text-align:end}
.sn-meter-bar{position:relative;display:block;height:6px;margin-top:6px;border-radius:3px;background:var(--sn-meter-track);box-shadow:inset 0 0 0 1px var(--sn-meter-edge);overflow:hidden}
.sn-meter-bar>i{display:block;height:100%;width:max(2px,calc(var(--p,0) * 1%));max-width:100%;border-radius:3px;background:var(--sn-meter-fill)}
.sn-meter.is-empty .sn-meter-bar>i{display:none}
.sn-meter.is-warn .sn-meter-bar>i{background:var(--warn)}
.sn-meter.is-bad .sn-meter-bar>i{background:var(--bad)}
.sn-meter.is-bad .sn-meter-v{color:var(--badTx)}
.sn-meter.is-nolimit .sn-meter-bar{display:none}
.sn-meter-m{margin-top:4px;color:var(--mut);font-size:var(--sn-fs-meta);line-height:1.4}
.sn-meter-m:empty{display:none}
/* Meters side by side from 900px, one column below; 16px between rows, 24px between the columns. */
.sn-meter-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:16px 24px}
@media(min-width:900px){.sn-meter-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* A.9 — TABLES, quieter and roomier: a 36px head in 12/500 --mut under a --line hairline (was --line3), 14px cells in
   rows of at least 44px, a hairline between rows, the hover wash kept. The first/last-column flush edges above stay. */
:where(:root[data-skin="v2"]) table.tbl{font-size:var(--sn-fs-body)}
:where(:root[data-skin="v2"]) table.tbl th{height:36px;padding-block:0;padding-inline:12px;border-bottom:1px solid var(--line);font:500 var(--sn-fs-meta)/1.3 var(--sans);letter-spacing:0;text-transform:none}
:where(:root[data-skin="v2"]) table.tbl td{height:44px;padding:10px 12px}
/* .dt — a date (or any count) column: end-aligned, tabular, never wrapped. .num keeps its old meaning. */
.dt{text-align:end;font-variant-numeric:tabular-nums;white-space:nowrap}
table.tbl th.dt,table.tbl td.dt{text-align:end}
/* .tr — a truncated cell: one line and an ellipsis; the page puts the full text in title="…". On a table cell the
   column width comes from --sn-tr-w (default 28ch), because max-width alone does not bind an auto-layout cell. */
.tr{display:block;max-width:var(--sn-tr-w,28ch);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
table.tbl td.tr,table.tbl th.tr{display:table-cell;width:var(--sn-tr-w,28ch);max-width:var(--sn-tr-w,28ch)}
/* The row-marker lane: a table that marks rows (new / unread / fresh) gives EVERY row the same 12px leading lane, so
   the name column stays one straight edge. .sn-lane is the empty lane; .sn-mark is the lane WITH the 6px dot (carry
   the words in a .vh child — the dot is never the only signal, and the page sets the row's name to 600). */
.sn-lane,.sn-mark{display:inline-block;flex:0 0 12px;width:12px;min-width:12px;height:12px;vertical-align:middle;position:relative}
.sn-mark::before{content:"";position:absolute;inset-block-start:3px;inset-inline-start:0;width:6px;height:6px;border-radius:50%;background:var(--accent)}
table.tbl td.sn-lane,table.tbl th.sn-lane,table.tbl td.sn-mark,table.tbl th.sn-mark{display:table-cell;width:12px;padding-inline:0}
table.tbl td.sn-mark::before{inset-block-start:calc(50% - 3px)}
/* The pager row is the table card's footer: a hairline above it and 12px of air. */
:where(:root[data-skin="v2"]) .card>.sn-pager,:where(:root[data-skin="v2"]) .sn-card-f{margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}

@media(max-width:720px){
  :where(:root[data-skin="v2"]) .card{padding:16px}
}

