
@font-face{font-family:"Outfit";font-style:normal;font-weight:600;font-display:swap;src:url("fonts/outfit-latin-600-normal.woff2") format("woff2")}
@font-face{font-family:"Outfit";font-style:normal;font-weight:800;font-display:swap;src:url("fonts/outfit-latin-800-normal.woff2") format("woff2")}
@font-face{font-family:"Inter";font-style:normal;font-weight:400;font-display:swap;src:url("fonts/inter-latin-400-normal.woff2") format("woff2")}
@font-face{font-family:"Inter";font-style:normal;font-weight:500;font-display:swap;src:url("fonts/inter-latin-500-normal.woff2") format("woff2")}
@font-face{font-family:"Inter";font-style:normal;font-weight:600;font-display:swap;src:url("fonts/inter-latin-600-normal.woff2") format("woff2")}
@font-face{font-family:"Inter";font-style:normal;font-weight:700;font-display:swap;src:url("fonts/inter-latin-700-normal.woff2") format("woff2")}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:400;font-display:swap;src:url("fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2")}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:600;font-display:swap;src:url("fonts/ibm-plex-mono-latin-600-normal.woff2") format("woff2")}


:root{color-scheme:dark;--bg:#0b0d11;--bg2:#0e1116;--panel:#12151c;--line:rgba(233,227,213,.22);--line-soft:rgba(233,227,213,.09);--line-strong:rgba(233,227,213,.38);--text:#f0ebdd;--head:#fbf8f0;--sub:#c0b8a5;--placeholder:#948d7d;--accent:#e2543e;--accent-hover:#ef6b52;--accent-ink:#ef6b52;--on-accent:#160906;--good:#57e389;--on-good:#06170b;--bad:#ef6b52;--warn:#e8b64a;--link:#ef8a72;--chip-bg:rgba(233,227,213,.04);--alert-bg-a:#4a1109;--alert-bg-b:#2b0b06;--alert-ink:#ffece5;--alert-sub:#f2b9a8;--alert-line:#7a2a18;--shadow-card:0 1px 0 rgba(244,239,226,.11) inset,0 14px 34px rgba(0,0,0,.35);--shadow-raised:0 1px 0 rgba(244,239,226,.16) inset,0 18px 44px rgba(0,0,0,.48);--shadow-overlay:0 8px 28px rgba(0,0,0,.55);--head-glow:0 0 60px rgba(244,239,226,.10);--q-glow:0 0 30px rgba(226,84,62,.45);--term-bg:#07090c;--term-text:#c3cdd6;--term-good:#57e389;--term-placeholder:#78838e;--term-line:rgba(195,205,214,.50);--dot:rgba(233,227,213,.045);--margin:#e2543e;--glow-a:rgba(226,84,62,.11);--glow-b:rgba(87,227,137,.05);--glow-c:rgba(226,84,62,.05);--margin-line:linear-gradient(to bottom,transparent,rgba(226,84,62,.4) 12%,rgba(226,84,62,.4) 88%,transparent);--display:"Outfit","Segoe UI",Helvetica,Arial,sans-serif;--sans:"Inter","Segoe UI",Helvetica,Arial,sans-serif;--mono:"IBM Plex Mono",ui-monospace,"Cascadia Code",Consolas,monospace;--dur-fast:120ms;--dur-base:200ms;--dur-slow:320ms;--ease:cubic-bezier(.2,.7,.3,1);--fs100:.75rem;--fs200:.8125rem;--fs300:.875rem;--fs400:1rem;--fs500:1.0625rem;--fs600:1.1875rem;--fs700:1.5rem;--fs800:1.875rem;--fs900:2.25rem;--lh-tight:1.15;--lh-snug:1.35;--lh-base:1.6;--track-label:.08em;--track-eyebrow:.16em;--sp1:.25rem;--sp2:.5rem;--sp3:.75rem;--sp4:1rem;--sp5:1.25rem;--sp6:1.5rem;--sp7:2rem;--sp8:2.5rem;--sp9:3rem;--r1:4px;--r2:8px;--r3:10px;--r4:12px;--r5:16px;--r-pill:999px;--shell-max:1000px;--hit:44px;--rail:232px;--header-h:72px;}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]){color-scheme:light;--bg:#f6f2e8;--bg2:#eee7d6;--panel:#fffefb;--line:rgba(29,23,16,.30);--line-soft:rgba(29,23,16,.14);--line-strong:rgba(29,23,16,.50);--text:#191309;--head:#100b05;--sub:#453c2d;--placeholder:#645b4d;--accent:#c9402b;--accent-hover:#a93521;--accent-ink:#a93521;--on-accent:#fff7f2;--good:#15724a;--on-good:#f2fff8;--bad:#b23a24;--warn:#8a5f05;--link:#b8442e;--chip-bg:rgba(29,23,16,.06);--alert-bg-a:#7f220d;--alert-bg-b:#5c150a;--alert-ink:#fff6f2;--alert-sub:#ffd9cc;--alert-line:#a33a22;--shadow-card:0 1px 0 rgba(255,255,255,.9) inset,0 1px 2px rgba(29,23,16,.07),0 10px 24px rgba(29,23,16,.10);--shadow-raised:0 1px 0 rgba(255,255,255,.95) inset,0 2px 4px rgba(29,23,16,.09),0 16px 34px rgba(29,23,16,.14);--shadow-overlay:0 6px 20px rgba(29,23,16,.14);--head-glow:none;--q-glow:none;--term-bg:#07090c;--term-text:#c3cdd6;--term-good:#57e389;--term-placeholder:#78838e;--term-line:rgba(195,205,214,.50);--dot:rgba(29,23,16,.06);--margin:#c9402b;--glow-a:rgba(201,64,43,.09);--glow-b:rgba(21,114,74,.05);--glow-c:rgba(201,64,43,.045);--margin-line:linear-gradient(to bottom,transparent,rgba(201,64,43,.5) 12%,rgba(201,64,43,.5) 88%,transparent);}}
:root[data-theme="light"]{color-scheme:light;--bg:#f6f2e8;--bg2:#eee7d6;--panel:#fffefb;--line:rgba(29,23,16,.30);--line-soft:rgba(29,23,16,.14);--line-strong:rgba(29,23,16,.50);--text:#191309;--head:#100b05;--sub:#453c2d;--placeholder:#645b4d;--accent:#c9402b;--accent-hover:#a93521;--accent-ink:#a93521;--on-accent:#fff7f2;--good:#15724a;--on-good:#f2fff8;--bad:#b23a24;--warn:#8a5f05;--link:#b8442e;--chip-bg:rgba(29,23,16,.06);--alert-bg-a:#7f220d;--alert-bg-b:#5c150a;--alert-ink:#fff6f2;--alert-sub:#ffd9cc;--alert-line:#a33a22;--shadow-card:0 1px 0 rgba(255,255,255,.9) inset,0 1px 2px rgba(29,23,16,.07),0 10px 24px rgba(29,23,16,.10);--shadow-raised:0 1px 0 rgba(255,255,255,.95) inset,0 2px 4px rgba(29,23,16,.09),0 16px 34px rgba(29,23,16,.14);--shadow-overlay:0 6px 20px rgba(29,23,16,.14);--head-glow:none;--q-glow:none;--term-bg:#07090c;--term-text:#c3cdd6;--term-good:#57e389;--term-placeholder:#78838e;--term-line:rgba(195,205,214,.50);--dot:rgba(29,23,16,.06);--margin:#c9402b;--glow-a:rgba(201,64,43,.09);--glow-b:rgba(21,114,74,.05);--glow-c:rgba(201,64,43,.045);--margin-line:linear-gradient(to bottom,transparent,rgba(201,64,43,.5) 12%,rgba(201,64,43,.5) 88%,transparent);}
/* The frosted surfaces, derived from the opaque tokens rather than being
   palette entries of their own. --panel stays exactly the value the contrast
   test measures body copy against; the glass is that value, thinned, so a
   surface can never drift away from the pair that was checked.

   The two themes are NOT thinned equally. Dark glass has a near-black page
   behind it and reads as glass at 72%. Light glass is near-white over cream,
   where thinning attacks the one thing that separates a card from the page —
   and "cream on cream" is a defect this palette has already been through once
   (see LIGHT in tokens.ts), so the light sheet stays dense and lets the blur,
   the border and the contact shadow carry the effect instead. */
:root{--glass-panel:color-mix(in srgb,var(--panel) 72%,transparent);--glass-well:color-mix(in srgb,var(--bg2) 66%,transparent)}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]){--glass-panel:color-mix(in srgb,var(--panel) 86%,transparent);--glass-well:color-mix(in srgb,var(--bg2) 80%,transparent)}}
:root[data-theme="light"]{--glass-panel:color-mix(in srgb,var(--panel) 86%,transparent);--glass-well:color-mix(in srgb,var(--bg2) 80%,transparent)}


:root[data-text="lg"]{font-size:112.5%}
:root[data-text="xl"]{font-size:125%}


*{box-sizing:border-box;margin:0}
html{-webkit-text-size-adjust:100%}
body{
  background-color:var(--bg);color:var(--text);
  font-family:var(--sans);font-size:var(--fs400);font-weight:400;line-height:var(--lh-base);
  min-height:100vh;
  /* A column, so the footer sits at the bottom of a short page (sign-in, a
     refusal) instead of floating mid-viewport under the content. */
  display:flex;flex-direction:column;
}
/* The page's own light.
 *
 * The app was deliberately flat for one release, and the surfaces above are
 * frosted now — which only reads as glass if there is something behind it to
 * refract. Three very low ambient washes do that: fixed to the viewport so
 * they do not travel with the scroll (a gradient that scrolls reads as a
 * texture, not as light), pointer-events off, and behind everything. This is
 * NOT the marketing site's ruled paper returning; there is no texture here,
 * only depth. Both themes define the glows — in light they are a whisper.
 */
body.shell::before{
  content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(70rem 45rem at 8% -12%,var(--glow-a),transparent 62%),
    radial-gradient(55rem 38rem at 108% 6%,var(--glow-c),transparent 60%),
    radial-gradient(50rem 32rem at 50% 118%,var(--glow-b),transparent 60%);
}
.shell-body{flex:1 0 auto;width:100%}
footer{flex-shrink:0}
a{color:var(--link);text-decoration:none;transition:color var(--dur-fast) var(--ease)}
a:hover{text-decoration:underline}
.wrap{max-width:var(--shell-max);margin:0 auto;padding:0 var(--sp5);position:relative;z-index:2}

/* Visually hidden, still read aloud — table captions and live-region text. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* For a cell holding one indivisible value. An ISO date is a single token to a
   reader but four break opportunities to the layout engine, so a narrow column
   split "2026-08-07" across two lines as "2026-08-" / "07" — which reads as two
   dates at a glance, in the one table where misreading a date is the whole
   cost. */
.nowrap{white-space:nowrap}

/* Keyboard users reach the content without walking the whole nav. */
.skip-link{position:absolute;left:-9999px;top:0;z-index:100;background:var(--panel);color:var(--head);border:1px solid var(--accent);border-bottom-right-radius:var(--r3);padding:var(--sp2) var(--sp4);font-family:var(--mono);font-size:var(--fs200)}
.skip-link:focus{left:0;text-decoration:none}


a:focus-visible,button:focus-visible,.btn:focus-visible,.toggle:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--r3)}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 18%,transparent)}
input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px;box-shadow:none}


/* Only a shell that actually carries navigation widens: a login screen or a
   single-page surface keeps the original reading measure. */
body.shell.has-nav{--shell-max:1240px}
/* The header floats over the content as you scroll — glass, not paint: the
   ruled paper shows through a strong blur. The mix stays ≥70% --bg so the
   worst case for anything read on it is within a whisker of the
   machine-checked page pairs, and browsers without backdrop-filter get the
   solid page colour rather than an unreadable wash. */
header{border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--bg) 78%,transparent);backdrop-filter:blur(14px) saturate(1.15);box-shadow:var(--shadow-overlay);position:sticky;top:0;z-index:10}
@supports not (backdrop-filter:blur(1px)){header{background:var(--bg)}}
.hd{display:flex;align-items:center;gap:var(--sp4);max-width:var(--shell-max);margin:0 auto;padding:var(--sp3) var(--sp5)}
.brand{display:flex;align-items:center;gap:var(--sp3);color:var(--head)}
.brand:hover{text-decoration:none}
/* The mark carries the top-left corner, so it is sized to be seen rather than
   to fit politely beside the controls — the owner asked for it bigger, and the
   header height follows it rather than the other way round. */
.brand-logo{height:36px;width:auto;display:block;color:var(--head)}
.brand-logo .node{fill:var(--accent)}
.brand-sub{font-family:var(--mono);font-weight:400;font-size:var(--fs100);text-transform:uppercase;letter-spacing:var(--track-eyebrow);color:var(--sub);align-self:center;padding-left:var(--sp2);border-left:1px solid var(--line)}
.hd-right{margin-left:auto;display:flex;align-items:center;gap:var(--sp4)}
.toggle{background:transparent;border:1px solid var(--line-strong);color:var(--sub);border-radius:var(--r-pill);font-family:var(--mono);font-size:var(--fs100);text-transform:uppercase;letter-spacing:var(--track-eyebrow);padding:var(--sp1) var(--sp3);cursor:pointer;margin:0;display:inline-flex;align-items:center;gap:var(--sp2);transition:border-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}
.toggle:hover{border-color:var(--accent);color:var(--head)}
/* One button, three states, and it draws BOTH faces so you can see which one
   is in force. "Auto" lights neither — which is the honest picture: the
   operating system is choosing, not you. The word stays beside the glyphs
   because no icon has ever meant "auto". */
.toggle-ic{display:inline-flex;align-items:center;gap:var(--sp1)}
.toggle-ic svg{display:block;width:15px;height:15px;padding:2px;box-sizing:content-box;border-radius:var(--r-pill);transition:background var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease)}
.toggle[data-mode="light"] .toggle-ic svg:first-child,.toggle[data-mode="dark"] .toggle-ic svg:last-child{background:var(--chip-bg);color:var(--head)}
.who{color:var(--sub);font-size:var(--fs200);font-family:var(--mono)}

.shell-body{max-width:var(--shell-max);margin:0 auto;padding:0 var(--sp5);position:relative;z-index:2}
.shell-main{min-width:0}
.sidebar ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:.12rem}
.sidebar a{display:block;padding:var(--sp2) var(--sp3);border-radius:var(--r2);color:var(--sub);font-size:var(--fs300);font-weight:500;border-left:2px solid transparent;transition:color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}
.sidebar a:hover{color:var(--head);background:var(--chip-bg);text-decoration:none}
.sidebar a[aria-current="page"]{color:var(--head);background:var(--chip-bg);border-left-color:var(--accent);font-weight:600}
/* Group headers are SEPARATORS, not more mono chrome: a rule above, clear
   air around, and a heavier sans voice so "Your work" and "Settings" read as
   sections at a glance — the owner flagged that the tracked-mono treatment
   made them vanish into the links. */
.sidebar .navgroup{font-family:var(--sans);font-weight:700;font-size:var(--fs200);letter-spacing:.02em;color:var(--head);border-top:1px solid var(--line-soft);margin:var(--sp5) var(--sp3) var(--sp2);padding-top:var(--sp4)}
.sidebar .navgroup:first-child{margin-top:0;border-top:0;padding-top:0}
/* The rail's glyphs. vertical-align carries the block layout (the collapsed
   strip), the margin carries the flex one (the rail, and any coarse pointer) —
   one rule for both, and a surface that names no icons is untouched by it. */
.nav-ic{display:inline-flex;align-items:center;vertical-align:-.28em;margin-right:var(--sp2);color:var(--sub);flex:none;transition:color var(--dur-fast) var(--ease)}
.nav-ic svg{display:block}
.sidebar a:hover .nav-ic{color:var(--head)}
.sidebar a[aria-current="page"] .nav-ic{color:var(--accent-ink)}

@media (min-width:1024px){
  body.has-nav .shell-body{display:grid;grid-template-columns:194px minmax(0,1fr);gap:var(--sp8);align-items:start}
  body.has-nav .sidebar{position:sticky;top:4.4rem;padding-top:var(--sp7)}
}
@media (max-width:1023px){
  .sidebar{margin:var(--sp4) 0 0;overflow-x:auto;border-bottom:1px solid var(--line-soft)}
  .sidebar ul{flex-direction:row;gap:var(--sp1);padding-bottom:.1rem}
  .sidebar a{white-space:nowrap;border-left:0;border-bottom:2px solid transparent;border-radius:var(--r2) var(--r2) 0 0}
  .sidebar a[aria-current="page"]{border-bottom-color:var(--accent)}
  .sidebar .navgroup{display:none}
}


@media (min-width:1024px){
  body.rail{padding-left:var(--rail);--shell-max:1240px}
  body.rail .shell-body{display:block}
  /* The top bar CAPS the rail, never the reverse (the owner's call on the
     corner): the header is pulled back across the body's rail offset so it
     spans the full viewport with the brand at its far left, holds a fixed
     height, and sits above the rail in the stack — the rail starts BELOW it. */
  body.rail header{margin-left:calc(-1*var(--rail));z-index:30}
  body.rail .hd{max-width:none;height:var(--header-h);padding-top:0;padding-bottom:0}
  body.rail .sidebar.rail{position:fixed;left:0;top:var(--header-h);bottom:0;width:var(--rail);margin:0;padding:var(--sp5) var(--sp3) var(--sp5);overflow-y:auto;border-right:1px solid var(--line);background:color-mix(in srgb,var(--panel) 78%,transparent);backdrop-filter:blur(14px) saturate(1.15);z-index:9;display:flex;flex-direction:column;gap:var(--sp2)}
  @supports not (backdrop-filter:blur(1px)){body.rail .sidebar.rail{background:var(--panel)}}
  body.rail .sidebar.rail ul{gap:.12rem}
  body.rail .sidebar a{min-height:var(--hit);display:flex;align-items:center}
  body.rail .sidebar a .nav-ic{margin-right:var(--sp3)}
}


details.menu{position:relative;display:inline-block}
details.menu>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:var(--sp1);border-radius:var(--r3);padding:var(--sp1) var(--sp2);min-height:var(--sp7);transition:color var(--dur-fast) var(--ease)}
details.menu>summary::-webkit-details-marker{display:none}
details.menu>summary::after{content:"\25BE";font-size:var(--fs100);color:var(--sub);transition:transform var(--dur-fast) var(--ease)}
details.menu>summary:hover{color:var(--head)}
details.menu[open]>summary{color:var(--head)}
details.menu[open]>summary::after{transform:rotate(180deg)}
.menu-panel{position:absolute;right:0;top:calc(100% + var(--sp2));min-width:264px;max-width:calc(100vw - 2*var(--sp4));background:color-mix(in srgb,var(--panel) 82%,transparent);backdrop-filter:blur(14px) saturate(1.15);border:1px solid var(--line-strong);border-radius:var(--r4);box-shadow:var(--shadow-overlay);padding:var(--sp3);z-index:60;display:flex;flex-direction:column;gap:var(--sp1);text-align:left}
@supports not (backdrop-filter:blur(1px)){.menu-panel{background:var(--panel)}}
.menu-id{padding:var(--sp1) var(--sp2) var(--sp2);border-bottom:1px solid var(--line-soft);margin-bottom:var(--sp1)}
.menu-id b{color:var(--head);font-size:var(--fs300)}
.menu-panel a.menu-link{display:block;padding:var(--sp2) var(--sp2);border-radius:var(--r2);color:var(--text);font-size:var(--fs300);font-weight:500}
.menu-panel a.menu-link:hover{background:var(--chip-bg);color:var(--head);text-decoration:none}
.menu-panel form{margin-top:var(--sp1)}
/* The notification bell. The count badge is live-polled; empty means read. */
.bell-count{display:inline-grid;place-items:center;min-width:1.15rem;height:1.15rem;padding:0 .25rem;border-radius:var(--r-pill);background:var(--accent);color:var(--on-accent);font-family:var(--mono);font-size:var(--fs100);line-height:1}
.bell-count:empty{display:none}
.bell-icon{display:block;color:var(--sub)}
details.menu[open] .bell-icon,details.menu>summary:hover .bell-icon{color:var(--head)}
.menu-panel a.notice{border-left:2px solid transparent}
.menu-panel a.notice.unread{border-left-color:var(--accent)}
.menu-panel .notice b{display:block;color:var(--head);font-size:var(--fs300)}

/* The monogram. Initials, never a photograph: there is no upload path in the
   product and an air-gapped install has nowhere to fetch an image from — so
   the one identity mark it can always draw is the one it draws. Decorative in
   both places it appears; the name is always beside it. */
.avatar{display:inline-grid;place-items:center;width:1.55rem;height:1.55rem;flex:none;border-radius:var(--r-pill);background:var(--accent);color:var(--on-accent);font-family:var(--mono);font-size:var(--fs100);font-weight:600;line-height:1;letter-spacing:0;text-transform:uppercase}
.avatar-lg{width:2.5rem;height:2.5rem;font-size:var(--fs300)}
.menu-id.has-avatar{display:flex;align-items:center;gap:var(--sp3)}
.menu-id-t{min-width:0}
/* The panel is where someone checks they are in the right entity before they
   authorise a filing, so it carries the entity and the switch — not just the
   account. The last block is the security snapshot: when this session began,
   and the way to the account page. */
.menu-entity,.menu-sec{display:flex;flex-direction:column;gap:var(--sp2);padding:var(--sp2);border-bottom:1px solid var(--line-soft);margin-bottom:var(--sp1)}
.menu-entity b{color:var(--head);font-size:var(--fs300)}
.menu-entity form{display:flex;align-items:center;gap:var(--sp2);margin:0}
.menu-entity select{width:100%;min-height:0;padding:var(--sp1) var(--sp2);font-size:var(--fs200)}
.menu-sec{gap:var(--sp1)}


h1{font-family:var(--display);color:var(--head);font-size:var(--fs800);font-weight:800;letter-spacing:-.02em;margin:var(--sp6) 0 var(--sp1);text-shadow:var(--head-glow)}
h2{font-family:var(--display);color:var(--head);font-size:var(--fs600);font-weight:600;margin:var(--sp7) 0 var(--sp3)}
h3{font-family:var(--display);color:var(--head);font-size:var(--fs500);font-weight:600;margin:var(--sp6) 0 var(--sp2)}
.eyebrow{font-family:var(--mono);font-size:var(--fs100);text-transform:uppercase;letter-spacing:var(--track-eyebrow);color:var(--sub)}
.sub{color:var(--sub)}.mono{font-family:var(--mono);font-size:.84em}
/* "There is nothing here" — an unset price, an absent date, an em-dash in a
   register. Italic so it reads as a different KIND of thing from a dim value,
   matching the portal's .empty. HQ drew all of these with a bare inline
   opacity between .45 and .6, which set no colour at all: the ink was --text
   composited toward the page, so the light theme rendered them between 2.79:1
   and 4.31:1 — every one of them below AA. */
.none{color:var(--sub);font-style:italic}
/* The secondary identifier under a name: a tenant id, a contact address. Was
   an inline opacity:.55 at .8rem — 2.79:1 at 12.8px, the worst pair in the
   product. */
.meta{display:block;color:var(--sub);font-size:var(--fs200);line-height:var(--lh-snug);margin-top:.1rem}
/* Doubled class on purpose. These are status colours, and they have to win
   wherever they land: the .card b rule below is (0,1,1) and beat a bare .bad
   (0,1,0), so the "overdue by 10 days" bold in the Deadlines card rendered as
   ordinary heading ink in light and near-WHITE in dark — the one row a
   compliance dashboard must not let you skim past was the one row with no colour
   on it. .bad.bad is (0,2,0) and wins on class count, without !important and
   without pinning this to .card. */
.good.good{color:var(--good)}.bad.bad{color:var(--bad)}.warn.warn{color:var(--warn)}
.lede{color:var(--sub);margin-bottom:var(--sp6)}
/* A heading that sits tight inside a card block — the failure explainer's
   title, directly under its eyebrow. */
h2.tight{margin:var(--sp1) 0}
/* A long unbroken value (an API key, an HMRC error string) must wrap, not
   set the width of its container. */
.wrapany{overflow-wrap:anywhere;word-break:break-all}


/* Frosted, not painted. The composite is ~74% --panel over the page, and both
   are near-neighbours in each theme (#12151c over #0b0d11 dark, #fffefb over
   #f6f2e8 light), so body copy on a card lands within a few tenths of the
   ratio the contrast test measures against solid --panel — which that test
   cannot see through a color-mix, hence this note. */
.card{
  position:relative;background:var(--glass-panel);border:1px solid var(--line);border-radius:var(--r5);
  padding:var(--sp6);margin-bottom:var(--sp5);box-shadow:var(--shadow-card);
  transition:border-color var(--dur-base) var(--ease);
}
.card b{color:var(--head)}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));gap:var(--sp4)}
/* Stat-tile grid — HQ's dashboard and the portal's summary both had their own
   copy of this at different minimums. */
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--sp5);margin-bottom:var(--sp7)}
.cards.small{grid-template-columns:repeat(auto-fit,minmax(140px,1fr))}
.narrow{max-width:440px}
/* A QR code stays black-on-white in both themes: a scanner needs the contrast
   the format assumes, and re-inking it to the palette would break it. The
   padding is the "quiet zone" the spec requires around the symbol. */
.qr{margin:var(--sp4) 0;text-align:center}
.qr svg{background:#fff;padding:10px;border-radius:var(--r3);max-width:220px;height:auto}

/* A centred single-purpose page: sign-in, activation, a one-question form. */
.login-wrap{max-width:400px;margin:7vh auto 0}
.login-wrap .brand{justify-content:center;margin-bottom:var(--sp2)}
.rowhead{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp4)}
.rowhead h2{margin-bottom:var(--sp2)}
.mt{margin-top:var(--sp3)}.tight{margin-top:var(--sp1)}

/* KPI tiles — label above, a big quiet number, an optional one-line meaning
   below (the monitoring-dashboard structure: title, value, description). */
.stat{border:1px solid var(--line);border-radius:var(--r4);padding:var(--sp5) var(--sp6);background:var(--glass-panel);box-shadow:var(--shadow-card)}
/* One blur declaration for the whole card family: the browser pays per element
   either way, but the sheet keeps a single place where the frosting is set and
   a single solid fallback for the browsers that cannot composite it. */
.card,.stat,.platform{backdrop-filter:blur(14px) saturate(1.15)}
@supports not (backdrop-filter:blur(1px)){.card,.stat,.platform{background:var(--panel)}}
.stat .l{color:var(--text);font-weight:600;font-size:var(--fs300)}
.stat .n{font-family:var(--sans);font-size:var(--fs700);font-weight:700;line-height:var(--lh-tight);color:var(--head);font-variant-numeric:tabular-nums;margin-top:var(--sp2)}
.stat .d{color:var(--sub);font-size:var(--fs200);margin-top:var(--sp1)}
/* Tiles keep their old markup order in HQ (.n then .l); flex order makes the
   label lead visually everywhere without touching every caller. */
.stat{display:flex;flex-direction:column;position:relative}
.stat .l{order:1}.stat .n{order:2}.stat .d{order:3}
/* The tile's status glyph sits in the corner and CONFIRMS the number; it never
   carries the meaning on its own (a red triangle and a red 0 say the same
   thing, and only one of them survives being read aloud). The label reserves
   room for it so a long one cannot run underneath. */
.stat-ic{position:absolute;top:var(--sp4);right:var(--sp5);display:inline-flex;color:var(--sub)}
.stat-ic.ok{color:var(--good)}.stat-ic.warn{color:var(--warn)}.stat-ic.bad{color:var(--bad)}
/* The glyph is the tile's FIRST child, so the label can reserve room for it by
   sibling alone — a tile without one (HQ's dashboard, the portal's summary) is
   left exactly as it was rather than carrying a gutter for nothing. */
.stat-ic~.l{padding-right:var(--sp7)}

/* A platform card leads with the mark, so a shelf of packs reads as products
   rather than as a list of sentences. */
.pack-head{display:flex;align-items:center;gap:var(--sp3);margin-bottom:var(--sp2)}
.pack-head b{flex:1;min-width:0}
.pack-head .brand-q{width:20px;height:20px;flex:none;color:var(--head)}
.brand-q .node{fill:var(--accent)}
.pack-status{display:flex;align-items:center;gap:var(--sp2);color:var(--sub);font-size:var(--fs200);margin:var(--sp1) 0 var(--sp3)}
.dot{width:.5rem;height:.5rem;flex:none;border-radius:var(--r-pill);background:var(--sub)}
.dot.ok{background:var(--good)}

/* A list whose marker is its own content — a step's pill, a check, a link. Both
   this and .w-full were referenced from markup in seven places across the
   dashboard and the portal and defined NOWHERE, so every one of those lists has
   been rendering with the browser's default disc and indent, and the sign-in
   button that asks to be full width never was. */
.nice{list-style:none;padding:0;margin:var(--sp4) 0;display:flex;flex-direction:column;gap:var(--sp3)}
.nice li{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--sp2)}
.nice li .meta{flex-basis:100%}
.w-full{width:100%}

/* Setup progress. The bar is a picture of a number that is also written out
   beside it ("2 of 5 done"), so it carries no meaning on its own — which is
   what lets it be purely decorative to a screen reader. */
.progress{height:8px;margin:var(--sp4) 0 var(--sp5);border:1px solid var(--line);border-radius:var(--r-pill);background:var(--chip-bg);overflow:hidden}
.progress-fill{height:100%;border-radius:var(--r-pill);background:linear-gradient(90deg,var(--accent),var(--accent-hover));transition:width var(--dur-slow) var(--ease)}
.setup-card .setup-next{color:var(--text);margin-bottom:var(--sp2)}

/* One square per platform, in a row. Fixed-width tiles rather than a stretchy
   grid: a shelf of products should look the same whether the tenancy owns one
   or six, and a single pack stretched across 1240px looks like a mistake. */
.platforms{display:flex;flex-wrap:wrap;gap:var(--sp4);margin-bottom:var(--sp7)}
.platform{
  flex:0 0 15rem;aspect-ratio:1;display:flex;flex-direction:column;gap:var(--sp2);
  padding:var(--sp4);border:1px solid var(--line);border-radius:var(--r5);
  background:var(--glass-panel);box-shadow:var(--shadow-card);
  transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease);
}
.platform:hover{border-color:var(--accent);box-shadow:var(--shadow-raised)}
.platform-top{display:flex;align-items:center;justify-content:space-between;gap:var(--sp2)}
.platform-top .brand-q{width:22px;height:22px;color:var(--head)}
.platform-name b{display:block;color:var(--head);font-family:var(--display);font-size:var(--fs600);line-height:var(--lh-tight)}
/* The full product line is context, not the label — two lines of it at most. */
.platform-name .meta{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* Whatever the name's length, the action sits on the floor of the tile. */
.platform .pack-status{margin-top:auto;margin-bottom:0}
.platform form{margin:0}
.platform button{margin-top:0}
.platform .backlink{text-align:center}

/* Rearranging the dashboard. The strip only exists while customising, so the
   ordinary page carries no chrome for a mode nobody is in. */
.widget-tools{display:flex;justify-content:flex-end;margin:var(--sp6) 0 var(--sp2)}
.widget-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--sp3);flex-wrap:wrap;margin:var(--sp5) 0 var(--sp2);padding:var(--sp2) var(--sp3);border:1px dashed var(--line-strong);border-radius:var(--r3);background:var(--chip-bg)}
.widget-bar .btnrow{gap:var(--sp2)}

/* Empty states: a list with nothing in it should say so, not render blank. */
.empty{border:1px dashed var(--line);border-radius:var(--r4);padding:var(--sp6) var(--sp5);color:var(--sub);text-align:center}
.empty b{display:block;color:var(--head);margin-bottom:var(--sp1)}

/* Contextual help — a disclosure, so the explanation is one keystroke away
   without occupying the page for people who already know. */
details.help{margin:var(--sp2) 0;border-left:2px solid var(--line);padding-left:var(--sp3)}
details.help>summary{cursor:pointer;color:var(--sub);font-family:var(--mono);font-size:var(--fs100);text-transform:uppercase;letter-spacing:var(--track-label);list-style:none}
details.help>summary::-webkit-details-marker{display:none}
details.help>summary::before{content:"?";display:inline-grid;place-items:center;width:1.05rem;height:1.05rem;border:1px solid var(--line-strong);border-radius:var(--r-pill);margin-right:var(--sp2);font-size:var(--fs100)}
details.help>summary:hover{color:var(--head)}
details.help[open]>summary{color:var(--head);margin-bottom:var(--sp2)}
details.help p{color:var(--sub);font-size:var(--fs300)}


/* --lh-snug, not the body's 1.6: a register is scanned down a column, and
   14px rows at 1.6 put more air between values than between the columns. */
table{width:100%;border-collapse:collapse;font-size:var(--fs300);line-height:var(--lh-snug)}
/* Sans headers, not mono. The uppercase mono-with-tracking header belongs to
   the terminal surfaces; on a data table it out-shouted the data and read as
   ledger cosplay (owner: change the font). Inter semibold caps at fs200 labels
   the column and then gets out of the way. */
th{font-family:var(--sans);font-weight:600;font-size:var(--fs200);text-transform:uppercase;letter-spacing:.05em;color:var(--sub);text-align:left;padding:var(--sp3) var(--sp2);border-bottom:1px solid var(--line)}
/* Rows breathe: a register is scanned, and 8px of vertical padding put four
   quarters closer together than the columns within one of them. */
td{padding:var(--sp3) var(--sp2);border-bottom:1px solid var(--line-soft);vertical-align:top}
tr:last-child td{border-bottom:none}
/* Tabular figures in the text face — mono is for ids and hashes, not sums. */
.num{font-variant-numeric:tabular-nums;text-align:right}
/* A table wider than its column scrolls inside itself; the page never does. */
.scroll{overflow-x:auto;max-width:100%}


/* The chip went from 9.6px to the 12px floor, so the padding and tracking come
   in to pay for it — a status column in a register must not get 25% wider for
   the sake of a legible label. */
.pill{display:inline-block;font-family:var(--mono);font-size:var(--fs100);line-height:var(--lh-snug);text-transform:uppercase;letter-spacing:var(--track-label);padding:.1rem var(--sp2);border-radius:var(--r-pill);border:1px solid var(--line);color:var(--sub);background:var(--chip-bg);transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease)}
/* The status ring was mixed at 45%, which computes to ~1.9:1 against the page —
   below the 3:1 WCAG 1.4.11 wants of a border that carries meaning. 70% is the
   weakest mix that clears it in both themes. */
.pill.ok{color:var(--good);border-color:color-mix(in srgb,var(--good) 70%,transparent)}
.pill.warn,.pill.preview,.pill.off{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 70%,transparent)}
.pill.wait{color:var(--accent-ink);border-color:color-mix(in srgb,var(--accent) 70%,transparent);animation:pulse-wait 2.4s var(--ease) infinite}
.pill.fail,.pill.bad{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 70%,transparent)}
.pill.neutral{color:var(--sub)}


/* inline-flex, not inline-block: the label centres vertically inside the
   44px hit area, so a button beside a link beside an inline form all sit on
   one line — the misalignment the owner flagged was mostly buttons whose
   heights were an accident of their padding. */
button,.btn{
  display:inline-flex;align-items:center;justify-content:center;font-family:var(--sans);font-weight:700;font-size:var(--fs300);
  border-radius:var(--r3);padding:var(--sp2) var(--sp5);min-height:var(--hit);cursor:pointer;text-decoration:none;
  transition:background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease);
}
button{background:var(--accent);color:var(--on-accent);border:1px solid var(--accent);margin-top:var(--sp3)}
button:hover{background:var(--accent-hover);border-color:var(--accent-hover)}
.btn{background:transparent;border:1px solid var(--line-strong);color:var(--head)}
.btn:hover{border-color:var(--accent);background:var(--chip-bg);text-decoration:none}
button.primary,.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
button.primary:hover,.btn.primary:hover{background:var(--accent-hover);border-color:var(--accent-hover)}
button.ghost,.btn.ghost{background:transparent;border:1px solid var(--line-strong);color:var(--head)}
button.ghost:hover,.btn.ghost:hover{border-color:var(--accent);background:var(--chip-bg)}
button.danger,.btn.danger{background:transparent;border:1px solid color-mix(in srgb,var(--bad) 70%,transparent);color:var(--bad)}
button.danger:hover,.btn.danger:hover{background:color-mix(in srgb,var(--bad) 12%,transparent);border-color:var(--bad)}
/* The affirmative half of an Approve/Decline pair: the decision that GOES
   FORWARD is verdict-green, its refusal stays wax-red — the same green/red
   the pills and validation vocabulary already speak. Filled like a primary
   (it is the pair's primary), never used for anything but consent. */
button.confirm,.btn.confirm{background:var(--good);border-color:var(--good);color:var(--on-good)}
button.confirm:hover,.btn.confirm:hover{background:color-mix(in srgb,var(--good) 86%,var(--on-good) 14%);border-color:color-mix(in srgb,var(--good) 86%,var(--on-good) 14%)}
/* Compact controls keep a 32px visual height at a fine pointer; the
   pointer:coarse layer raises them to the full hit size on touch. */
button.small,.btn.small{padding:var(--sp1) var(--sp3);font-size:var(--fs200);font-weight:600;margin-top:0;min-height:calc(var(--hit) - var(--sp3))}
/* Disabled used to be opacity:.5, which took the filled primary's label to
   2.13:1 and was the only signal besides the cursor. A primary that demotes to
   a flat ghost says "not now" more clearly than a translucent one, and every
   colour in it is a token that has been measured. */
button[disabled],.btn[disabled]{background:var(--chip-bg);border-color:var(--line);color:var(--sub);box-shadow:none;cursor:not-allowed}
button[disabled]:hover,.btn[disabled]:hover{background:var(--chip-bg);border-color:var(--line);color:var(--sub)}
/* One row, one baseline: a bare button's top margin belongs to a button
   standing alone under a form, never to one sitting in a row beside its
   fellows — that stray margin was the crooked Approve/Decline pair. The gap
   is also the touch separation between adjacent targets. */
.btnrow{display:flex;gap:var(--sp3);flex-wrap:wrap;align-items:center}
.btnrow button{margin-top:0}
/* …but zeroing the buttons' margins left the ROW itself with none: a submit
   row that concludes a form sat flush against the field above it — the
   approval card's note input touched Approve/Decline at 0px. The row, not
   its buttons, owns the stand-off. Collapses with a .field's bottom margin,
   so stacked layouts don't double up. */
form .btnrow{margin-top:var(--sp4)}
/* A control sitting in a row (the year picker, list filters) sizes to its
   content — the 100% form default is for stacked field layouts. */
.btnrow select,.btnrow input{width:auto}
form.inline{display:inline}form.inline+form.inline{margin-left:var(--sp3)}
.backlink{font-family:var(--mono);font-size:var(--fs200);color:var(--sub)}


label{display:block;font-family:var(--mono);font-size:var(--fs100);text-transform:uppercase;letter-spacing:var(--track-label);color:var(--sub);margin:var(--sp3) 0 var(--sp1)}
/* A fieldset is a grouping device, not a drawn box — the UA border belongs to
   1997. Its legend takes the same caption voice as a label. */
fieldset{border:0;padding:0;margin:0;min-width:0}
legend{display:block;font-family:var(--mono);font-size:var(--fs100);text-transform:uppercase;letter-spacing:var(--track-label);color:var(--sub);margin:var(--sp3) 0 var(--sp1);padding:0}
input,select{width:100%;background:var(--bg2);color:var(--head);border:1px solid var(--line-strong);border-radius:var(--r3);padding:var(--sp2) var(--sp3);font-family:var(--mono);font-size:var(--fs300);transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
input[type=checkbox],input[type=radio]{width:auto}
/* A textarea is a form control first. It used to be a terminal surface
   wholesale, which put a black mono box on the cream feedback page — the
   product wearing the backend's clothes. Prose textareas now match every
   other input; the ones that hold STRUCTURED data (a JSON/CSV paste, an
   evidence template) opt into the terminal with .code, where mono alignment
   and a dark field are the point rather than an accident. */
textarea{width:100%;min-height:150px;background:var(--bg2);color:var(--head);border:1px solid var(--line-strong);border-radius:var(--r3);padding:var(--sp3) var(--sp3);font-family:var(--sans);font-size:var(--fs300);line-height:1.5;resize:vertical;transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
/* --line over #07090c computes to ~1.03:1, so the terminal field needs the
   terminal's own edge token or it has no visible border at all. */
textarea.code{background:var(--term-bg);color:var(--term-text);border-color:var(--term-line);font-family:var(--mono)}
/* Placeholders had no rule at all, so the UA default applied: 4.4:1 on an
   input, and 1.03:1 on a textarea — which is why the portal's evidence
   template, which is shown AS a placeholder, looked like an empty black box in
   the light theme. Two rules, because the two control families sit on opposite
   backgrounds. The opacity:1 is load-bearing: Firefox applies its own .54 to
   ::placeholder and would put the defect straight back. */
input::placeholder,select::placeholder,textarea::placeholder{color:var(--placeholder);opacity:1}
textarea.code::placeholder{color:var(--term-placeholder);opacity:1}
.field{margin-bottom:var(--sp4)}
.field label{font-family:var(--mono)}
.checks{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--sp2) var(--sp4)}
.checks label{display:flex;gap:var(--sp2);align-items:center;text-transform:none;letter-spacing:0;font-family:var(--sans);font-size:var(--fs300);color:var(--text)}
/* A label sitting on the same line as its control — "Paid until [date]". The
   block label rule above is a mono uppercase caption, so four places in HQ
   were overriding it back to sentence case inline; this is that override,
   once. Same shape as .checks label, which solved it for checkboxes. */
label.inline{display:flex;align-items:center;gap:var(--sp1);text-transform:none;letter-spacing:0;font-family:var(--sans);font-size:var(--fs300);font-weight:600;color:var(--text);margin:var(--sp3) 0 var(--sp1)}
.filepick{display:flex;align-items:center;gap:var(--sp3);flex-wrap:wrap;margin:var(--sp1) 0 var(--sp1)}
.filepick input[type=file]{width:auto;border:0;padding:0;font-size:var(--fs200);color:var(--sub)}
.file-name{font-family:var(--mono);font-size:var(--fs200);color:var(--good)}
.hint{color:var(--sub);font-size:var(--fs200);margin:var(--sp1) 0 var(--sp1)}


/* Border mixes are 70% (see PILL_CSS); the low percentages below are BACKGROUND
   washes, where a faint tint is the whole point. */
.flash{border:1px solid color-mix(in srgb,var(--good) 70%,transparent);color:var(--good);border-radius:var(--r3);padding:var(--sp2) var(--sp3);font-family:var(--mono);font-size:var(--fs300);margin:var(--sp3) 0;background:color-mix(in srgb,var(--good) 8%,transparent);animation:flash-in var(--dur-slow) var(--ease)}
.flash.err{border-color:color-mix(in srgb,var(--bad) 70%,transparent);color:var(--bad);background:color-mix(in srgb,var(--bad) 8%,transparent)}
.flash:empty{display:none;margin:0}
.note{border-radius:var(--r3);padding:var(--sp4) var(--sp4);margin-bottom:var(--sp5);border:1px solid var(--line)}
.note.good{background:color-mix(in srgb,var(--good) 10%,transparent);border-color:color-mix(in srgb,var(--good) 70%,transparent)}
.note.bad{background:color-mix(in srgb,var(--bad) 10%,transparent);border-color:color-mix(in srgb,var(--bad) 70%,transparent)}
/* The attention hero. HMRC's late-submission regime fines £200 at the fourth
   point, so a return that is overdue is not one tinted note among other tinted
   notes — it is its own object, in its own ink, carrying the single action
   that clears it. A SOLID gradient, never glass: what is behind this panel
   must never affect whether the fine warning can be read. It stays dark in
   both themes, because the one thing on the page that should not look like
   paper is the thing that costs money. */
.alert-hero{background:linear-gradient(135deg,var(--alert-bg-a),var(--alert-bg-b));border:1px solid var(--alert-line);border-radius:var(--r5);padding:var(--sp6);margin-bottom:var(--sp5);color:var(--alert-ink);box-shadow:var(--shadow-card)}
.alert-hero h2{color:var(--alert-ink);font-size:var(--fs700);margin:var(--sp2) 0 var(--sp3);text-shadow:none}
.alert-hero p{color:var(--alert-ink)}
.alert-hero b{color:var(--alert-ink)}
.alert-hero .eyebrow,.alert-hero .hint{color:var(--alert-sub)}
/* The accent cannot appear on this surface: #c9402b on the light hero measures
   1.99:1 — a red button on a red panel. The ink fills the button instead. */
.btn.alert-cta{background:var(--alert-ink);border-color:var(--alert-ink);color:var(--alert-bg-b)}
.btn.alert-cta:hover{background:var(--alert-sub);border-color:var(--alert-sub);color:var(--alert-bg-b)}
.key{font-family:var(--mono);word-break:break-all;background:var(--term-bg);color:var(--term-text);border:1px solid var(--term-line);border-radius:var(--r2);padding:var(--sp3) var(--sp3);display:block;margin-top:var(--sp2)}
/* The one-time API key itself. This used to be --good, which flips to a dark
   green in the light theme while --term-bg stays #07090c in both — 3.36:1 on
   the single string the user gets exactly one chance to copy. */
.key .val{color:var(--term-good)}
.secret{display:flex;gap:var(--sp2);align-items:center;margin:var(--sp2) 0}
.secret code{flex:1;font-family:var(--mono);padding:var(--sp2) var(--sp3);border-radius:var(--r2);background:var(--term-bg);color:var(--term-text);border:1px solid var(--term-line);font-size:var(--fs300);word-break:break-all}
/* --lh-snug, not the body's 1.6: a footer is two or three short mono lines
   under a rule, and at 1.6 they sprawl far enough apart to read as separate
   blocks rather than one quiet note. The rule above it is --line, not
   --line-soft, and the clearance above is the largest step on the scale —
   the footer is a deliberate colophon, not text that ran out of page. */
footer{color:var(--sub);font-size:var(--fs200);font-family:var(--mono);line-height:var(--lh-snug);padding:var(--sp7) var(--sp5) var(--sp8);border-top:1px solid var(--line);margin-top:var(--sp9)}
/* Every footer in the product carries .wrap, whose margin:0 auto and
   padding:0 var(--sp5) SILENTLY won the specificity contest against the
   element rule above — which is why the footer always sat hard against the
   content with its rule touching the text ("not spaced out enough"). The
   compound selector outranks .wrap and keeps the centring. */
footer.wrap{margin:var(--sp9) auto 0;padding:var(--sp7) var(--sp5) var(--sp8)}
footer a{color:var(--sub);text-decoration:underline;text-underline-offset:.2em}
footer a:hover{color:var(--head)}
/* The evidence page lists its API endpoints here as code chips; without a gap
   they collide with the line above and with each other when they wrap. */
footer code{display:inline-block;margin:.15rem .1rem 0 0}


.chart{width:100%;height:auto;max-height:240px;display:block;margin:var(--sp4) 0;animation:fade-up var(--dur-slow) var(--ease)}
.chart .ch-grid{stroke:var(--line-soft)}
.chart .ch-axis{stroke:var(--line)}
.chart .ch-line{fill:none;stroke:var(--accent);stroke-width:2.5;stroke-linejoin:round;stroke-linecap:round}
.chart .ch-dot{fill:var(--accent)}
.chart .ch-fill-a{stop-color:color-mix(in srgb,var(--accent) 28%,transparent)}
.chart .ch-fill-b{stop-color:transparent}
.chart text{font-family:var(--sans)}
.chart .ch-key{fill:var(--sub)}
.chart .ch-ylabel{fill:var(--sub)}
.chart .ch-amount{fill:var(--head);font-weight:600}

/* The deadline timeline: swimlanes on a shared time axis. Dots are the data
   and wear status colour; every word stays in text tokens. The surface-colour
   stroke is the ring that keeps a dot legible over a gridline. */
/* The height cap is what keeps the type quiet: an uncapped width-scaled SVG
   renders its 12px labels at twice the table's size. Capped, the drawing
   scales down to the cap and the text lands beside the body size — the same
   mechanism the trend chart relies on. */
.chart.tl{max-height:260px}
.chart .tl-lane{fill:var(--head);font-weight:600}
.chart .tl-past{fill:color-mix(in srgb,var(--text) 4%,transparent)}
.chart .tl-today{stroke:var(--line-strong)}
.chart .tl-dot{fill:var(--good);stroke:var(--panel);stroke-width:2}
.chart .tl-dot.warn{fill:var(--warn)}
.chart .tl-dot.bad{fill:var(--bad)}
.chart .tl-note{fill:var(--head);font-weight:600}

/* The regulator's declaration: set apart, in the accent, never skimmable
   as ordinary body copy — a person is about to make a legal statement. */
.declaration{border:1px solid color-mix(in srgb,var(--accent) 70%,transparent);border-left-width:3px;border-radius:var(--r3);background:color-mix(in srgb,var(--accent) 6%,transparent);padding:var(--sp4);margin:var(--sp4) 0}
.declaration .decl-text{color:var(--head);font-weight:600;margin:var(--sp2) 0}
.declaration .checks{margin-top:var(--sp3)}

/* One chip per VAT period — status-tinted like a monitoring card. */
.period-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,220px));gap:var(--sp3);margin:var(--sp4) 0}
.period-card{border:1px solid var(--line);border-radius:var(--r4);padding:var(--sp3) var(--sp4);background:var(--glass-well);backdrop-filter:blur(14px) saturate(1.15)}
@supports not (backdrop-filter:blur(1px)){.period-card{background:var(--bg2)}}
.period-card .pk{font-family:var(--mono);font-size:var(--fs200);color:var(--sub)}
.period-card .amt{font-size:var(--fs600);font-weight:700;color:var(--head);font-variant-numeric:tabular-nums;margin-top:var(--sp1)}
.period-card .st{font-size:var(--fs200);color:var(--sub);margin-top:var(--sp1)}
.period-card.filed{border-color:color-mix(in srgb,var(--good) 60%,transparent);background:color-mix(in srgb,var(--good) 7%,transparent)}
.period-card.overdue{border-color:color-mix(in srgb,var(--bad) 60%,transparent);background:color-mix(in srgb,var(--bad) 7%,transparent)}
.period-card.due{border-color:color-mix(in srgb,var(--accent) 60%,transparent);background:color-mix(in srgb,var(--accent) 6%,transparent)}

/* The posture runway: one time-bar per platform on a shared axis, HTML all the
   way down so it lays out like text and can never scale into a monolith. The
   left tenth is the past; the vertical rule at its edge is today. Bars grow
   from today to the due date and wear status colour; the words beside them
   stay in text tokens. */
.runway{display:grid;grid-template-columns:minmax(150px,200px) 1fr;column-gap:var(--sp5);align-items:center;margin:var(--sp4) 0}
.rw-axis{position:relative;height:1.15rem}
.rw-tick{position:absolute;top:0;transform:translateX(-50%);font-size:var(--fs100);color:var(--sub);white-space:nowrap}
.rw-tick.today{color:var(--head);font-weight:600}
/* The final tick sits at left:100% — centred it would hang half outside the
   runway column, the exact overflow the SVG timeline end-anchors away. */
.rw-tick.end{transform:translateX(-100%)}
.rw-key{padding:var(--sp2) 0;line-height:var(--lh-snug);min-width:0}
.rw-key .meta{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rw-cell{position:relative;height:44px}
.rw-cell i{display:block}
/* Faint quarter-lines so the rows and the axis agree on where time is. */
.rw-grid{position:absolute;top:6px;bottom:6px;width:1px;background:var(--line-soft)}
.rw-today{position:absolute;top:3px;bottom:3px;width:1px;background:var(--line-strong)}
.rw-past{position:absolute;top:0;bottom:0;left:0;background:color-mix(in srgb,var(--text) 4%,transparent);border-radius:var(--r2) 0 0 var(--r2)}
.rw-track{position:absolute;top:50%;height:6px;transform:translateY(-50%);border-radius:3px;background:color-mix(in srgb,var(--text) 7%,transparent)}
.rw-bar{position:absolute;top:50%;height:6px;transform:translateY(-50%);border-radius:3px}
.rw-bar.ok{background:var(--good)}.rw-bar.warn{background:var(--warn)}.rw-bar.bad{background:var(--bad)}
.rw-dot{position:absolute;top:50%;width:10px;height:10px;border-radius:var(--r-pill);transform:translate(-50%,-50%);box-shadow:0 0 0 2px var(--panel)}
.rw-dot.ok{background:var(--good)}.rw-dot.warn{background:var(--warn)}.rw-dot.bad{background:var(--bad)}
.rw-val{position:absolute;top:50%;transform:translateY(-50%);font-size:var(--fs200);line-height:1.3;white-space:nowrap}
.rw-val.flip{transform:translate(-100%,-50%);text-align:right}
.rw-val .meta{margin-top:0}
.rw-none{position:absolute;top:50%;transform:translateY(-50%);font-size:var(--fs300);color:var(--sub)}
@media (max-width:1023px){.runway{grid-template-columns:110px 1fr;column-gap:var(--sp3)}}


.stepper{display:flex;flex-wrap:wrap;gap:var(--sp2);list-style:none;padding:0;margin:var(--sp2) 0 var(--sp6);font-family:var(--mono);font-size:var(--fs100);text-transform:uppercase;letter-spacing:var(--track-label)}
.stepper li{display:flex;align-items:center;gap:var(--sp2);color:var(--sub)}
.stepper li::before{content:attr(data-n);display:grid;place-items:center;width:1.4rem;height:1.4rem;border-radius:var(--r-pill);border:1px solid var(--line-strong);font-size:var(--fs100)}
.stepper li.on{color:var(--head)}.stepper li.on::before{border-color:var(--accent);color:var(--accent-ink)}
.stepper li.done::before{content:"✓";border-color:color-mix(in srgb,var(--good) 70%,transparent);color:var(--good)}
.stepper li+li{margin-left:var(--sp1)}
.choice{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--sp3);margin:var(--sp2) 0}
.choice a{display:block;background:var(--panel);border:1px solid var(--line);border-radius:var(--r4);padding:var(--sp4) var(--sp4);box-shadow:var(--shadow-card);transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease)}
.choice a:hover{border-color:var(--accent);box-shadow:var(--shadow-raised);text-decoration:none}
.choice b{color:var(--head);display:block;margin-bottom:var(--sp1)}


@keyframes fade-up{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@keyframes flash-in{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
/* "Waiting" pulses its RING, not its whole self. Animating opacity took the
   chip's label to 2.33:1 at the trough, twice every 2.4 seconds — an infinite
   animation that spent half its life illegible. */
@keyframes pulse-wait{0%,100%{border-color:color-mix(in srgb,var(--accent) 70%,transparent)}50%{border-color:var(--accent)}}
@keyframes live-pulse{from{background:color-mix(in srgb,var(--accent) 14%,transparent)}to{background:transparent}}
@keyframes card-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
@keyframes welcome-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
@keyframes welcome-rule{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes panel-pop{from{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:none}}
.shell-main{animation:fade-up var(--dur-slow) var(--ease)}
/* A region the live poller just replaced gets one brief accent wash, so a
   change that arrives while you are reading is noticed rather than silent. */
.live-updated{animation:live-pulse var(--dur-slow) var(--ease)}
/* Opt-in card entrance: a container marked .anim raises its first cards in a
   short stagger. "backwards" keeps a delayed card invisible until its turn —
   without it the card flashes at full opacity first. Purposeful, brief, and
   fully neutralised by both motion switches. */
.anim>.card{animation:card-in var(--dur-base) var(--ease) backwards}
.anim>.card:nth-child(2){animation-delay:60ms}
.anim>.card:nth-child(3){animation-delay:120ms}
.anim>.card:nth-child(n+4){animation-delay:160ms}
details.menu[open]>.menu-panel{animation:panel-pop var(--dur-fast) var(--ease)}
/* The dashboard's greeting IS the headline — a person is welcomed, not shown
   their own letterhead. The entity they are filing for sits beneath in a
   quiet line with the switch, and an accent rule draws itself under the
   block. Both movements die under either motion switch. */
.welcome{position:relative;padding-bottom:var(--sp4);margin-bottom:var(--sp5)}
.welcome::after{content:"";position:absolute;left:0;bottom:0;width:64px;height:2px;border-radius:2px;background:var(--accent);transform-origin:left;animation:welcome-rule var(--dur-slow) var(--ease) backwards;animation-delay:120ms}
/* The greeting outranks every page title (--fs900, a step above h1) and clears
   the sticky header by a full --sp8 — at --sp6 it read as tucked under the
   toolbar rather than opening the page. */
h1.welcome-hello{font-size:var(--fs900);margin-top:var(--sp8);animation:welcome-in var(--dur-base) var(--ease) backwards}
.welcome-entity{color:var(--sub);font-size:var(--fs300);display:flex;align-items:center;gap:var(--sp2);flex-wrap:wrap;margin-top:var(--sp2);animation:welcome-in var(--dur-base) var(--ease) backwards;animation-delay:60ms}
.welcome-entity b{color:var(--head)}
.welcome-entity .switcher{display:inline-flex;align-items:center;gap:var(--sp2)}
.welcome-entity select{width:auto;min-height:0;padding:var(--sp1) var(--sp2);font-size:var(--fs200)}


/* ── tablet ──────────────────────────────────────────────────────────────
 * The 641–1239px band had no rules at all: between a phone and a wide desktop
 * the layout was simply the desktop layout. The shell collapses here (see
 * SHELL_CSS) and the content grid tightens so cards do not strand at two
 * columns with a third of the row empty.
 */
@media (max-width:1023px){
  .grid{grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:var(--sp3)}
  /* h1 used to step to 1.7rem here — a 3px change nobody could see, and a size
     that belonged to no scale. It now holds --fs800 until the phone breakpoint,
     where it steps a whole step to --fs700. */
}

/* ── narrow screens ──────────────────────────────────────────────────────
 * Every app surface renders through this one stylesheet, and until this block
 * existed none of them had a single breakpoint: the header is a nowrap flex
 * row, so on a phone the brand plus the theme toggle, operator name and
 * sign-out ran past the viewport and the whole PAGE scrolled sideways — the
 * layout defect that reads as "broken", not merely "tight".
 */
@media (max-width:640px){
  .wrap,.hd,.shell-body{padding-left:var(--sp4);padding-right:var(--sp4)}
  .hd{flex-wrap:wrap;gap:var(--sp2) var(--sp3)}
  .hd-right{flex-wrap:wrap;gap:var(--sp2) var(--sp3);row-gap:var(--sp2)}
  /* A header dropdown anchored to its own summary runs off the LEFT edge of a
     phone: the panel is wider than the distance from that summary back to the
     gutter, so the bell's panel started at -74px and the identity panel's name
     and entity were simply off-screen. Making the header's right cluster the
     offset parent hangs both panels from the viewport's right gutter instead,
     where they fit — and lines them up with each other. */
  .hd-right{position:relative}
  details.menu{position:static}
  .brand-sub{padding-left:var(--sp2)}
  h1{font-size:var(--fs700);margin-top:var(--sp5)}
  /* The greeting steps down a whole step too — its selector outranks the h1
     rule above, so without this it would stay 36px on a 375px screen. */
  h1.welcome-hello{font-size:var(--fs800);margin-top:var(--sp6)}
  h2{font-size:var(--fs500)}
  /* Tighter gutters on a phone; the corner is --r5 at every width, because a
     card that changes shape with the viewport is a card drawn twice. */
  .card{padding:var(--sp4)}
  .rowhead{flex-wrap:wrap;gap:var(--sp1)}
  /* Keep a wide table inside its own scroller: the page itself must never
   * scroll horizontally, but a register table legitimately can. */
  table{display:block;width:100%;max-width:100%;overflow-x:auto}
  /* A long unbroken identifier (LEI, package name) must not set the floor
   * width of the document. */
  .key,.mono,.secret code{overflow-wrap:anywhere}
}



@media (pointer:coarse){
  button.small,.btn.small{min-height:var(--hit);padding:var(--sp2) var(--sp4)}
  .toggle{min-height:var(--hit);padding:var(--sp2) var(--sp3)}
  details.menu>summary{min-height:var(--hit)}
  .sidebar a{min-height:var(--hit);display:flex;align-items:center}
  input,select{min-height:var(--hit)}
  input[type=checkbox],input[type=radio]{width:1.4rem;height:1.4rem;min-height:0}
  .menu-panel a.menu-link{min-height:var(--hit);display:flex;align-items:center}
}


@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
}
:root[data-motion="reduce"] *,:root[data-motion="reduce"] *::before,:root[data-motion="reduce"] *::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
