/* ==========================================================================
   Atlas by GSM Deck — portal stylesheet
   Single self-hosted stylesheet. CSP-safe: no CSS imports, no url() to other
   origins, no inline styles required anywhere in the Jinja templates.
   (The words "no CSS imports" are deliberate: the guard in
   tests/test_atlas_portal.py is a blunt substring check for the at-rule, and
   a comment claiming the sheet does not use it would trip that check.)
   Serve from your own origin, e.g. /static/css/atlas.css
   Requires: font-src 'self' added to the policy.
   ========================================================================== */

/* --- 1. Self-hosted fonts ------------------------------------------------
   Download once, convert to woff2, serve from /static/fonts/.
   Michroma  — display only (headings, big numbers, brand)
   Archivo   — body text and UI
   JetBrains Mono — labels, IDs, IMEIs, amounts
   Subset to latin to keep them small. No CDN, no Google Fonts link.
-------------------------------------------------------------------------- */
@font-face{font-family:"Michroma";src:url("/static/fonts/michroma-400.woff2") format("woff2");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Archivo";src:url("/static/fonts/archivo-400.woff2") format("woff2");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"Archivo";src:url("/static/fonts/archivo-500.woff2") format("woff2");font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"Archivo";src:url("/static/fonts/archivo-700.woff2") format("woff2");font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:"JetBrains Mono";src:url("/static/fonts/jetbrains-mono-400.woff2") format("woff2");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"JetBrains Mono";src:url("/static/fonts/jetbrains-mono-500.woff2") format("woff2");font-weight:500;font-style:normal;font-display:swap}

/* --- 2. Tokens -----------------------------------------------------------
   Dark is the brand default. Light exists and must work.
   Theme resolution order:
     1. :root                          -> dark values (brand default)
     2. @media (prefers-color-scheme:light) -> follows the OS
     3. html[data-theme="light"|"dark"]  -> explicit server-rendered override
   The override comes from a cookie, so it works with JS disabled.
-------------------------------------------------------------------------- */
:root{
  /* surface */
  --bg:#0F1114;
  --surface:#16191D;
  --surface-2:#1B1F25;
  --line:#23282F;
  --line-2:#2A2F36;
  --line-3:#1C2027;

  /* text */
  --text:#FFFFFF;
  --text-2:#E4E8EC;
  --text-3:#C6CCD3;
  --text-dim:#9BA4AE;
  --text-mute:#6B7580;
  --text-faint:#3A4149;

  /* brand */
  --accent:#FF8A1E;          /* signal amber — one job per screen */
  --accent-strong:#FFA451;
  --accent-soft:rgba(255,138,30,.08);
  --on-accent:#0F1114;        /* text on an amber fill, both themes */

  /* status */
  --good:#4ED17A;
  --warn:#FFD24A;
  --bad:#FF6B5C;
  --info:#7FB2FF;
  --good-soft:rgba(78,209,122,.10);
  --warn-soft:rgba(255,210,74,.10);
  --bad-soft:rgba(255,107,92,.10);

  /* brand mark planes (inline SVG uses these) */
  --mark-frame:var(--text);
  --mark-base:var(--accent);

  /* chrome */
  --header-bg:rgba(15,17,20,.92);
  --focus:#FF8A1E;

  /* type */
  --font-display:"Michroma",ui-sans-serif,system-ui,sans-serif;
  --font-body:"Archivo",ui-sans-serif,system-ui,-apple-system,sans-serif;
  --font-mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  /* space — 4px base */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px;
  --s7:32px; --s8:40px; --s9:52px; --s10:72px; --s11:96px;

  /* geometry — the brand is squared off. No rounded corners anywhere. */
  --radius:0;
  --chamfer:10px;            /* cut-corner size on primary buttons */
  --rule:1px;
  --shell:1240px;
  --shell-narrow:520px;
}

@media (prefers-color-scheme:light){
  :root{
    --bg:#F1F3F5; --surface:#FFFFFF; --surface-2:#F7F9FA;
    --line:#E2E6EA; --line-2:#D5DAE0; --line-3:#EDF0F2;
    --text:#16191D; --text-2:#2A2F36; --text-3:#3E4650;
    --text-dim:#5A636D; --text-mute:#8A939E; --text-faint:#A8B0B8;
    --accent:#E8760C; --accent-strong:#FF8A1E; --accent-soft:rgba(232,118,12,.08);
    --good:#1E9E57; --warn:#B47B00; --bad:#D0362B; --info:#1B6FD1;
    --good-soft:rgba(30,158,87,.09); --warn-soft:rgba(180,123,0,.09); --bad-soft:rgba(208,54,43,.09);
    --mark-frame:var(--text); --mark-base:var(--accent);
    --header-bg:rgba(241,243,245,.92); --focus:#E8760C;
  }
}
html[data-theme="dark"]{
  --bg:#0F1114; --surface:#16191D; --surface-2:#1B1F25;
  --line:#23282F; --line-2:#2A2F36; --line-3:#1C2027;
  --text:#FFFFFF; --text-2:#E4E8EC; --text-3:#C6CCD3;
  --text-dim:#9BA4AE; --text-mute:#6B7580; --text-faint:#3A4149;
  --accent:#FF8A1E; --accent-strong:#FFA451; --accent-soft:rgba(255,138,30,.08);
  --good:#4ED17A; --warn:#FFD24A; --bad:#FF6B5C; --info:#7FB2FF;
  --good-soft:rgba(78,209,122,.10); --warn-soft:rgba(255,210,74,.10); --bad-soft:rgba(255,107,92,.10);
  --mark-frame:var(--text); --mark-base:var(--accent);
  --header-bg:rgba(15,17,20,.92); --focus:#FF8A1E;
}
html[data-theme="light"]{
  --bg:#F1F3F5; --surface:#FFFFFF; --surface-2:#F7F9FA;
  --line:#E2E6EA; --line-2:#D5DAE0; --line-3:#EDF0F2;
  --text:#16191D; --text-2:#2A2F36; --text-3:#3E4650;
  --text-dim:#5A636D; --text-mute:#8A939E; --text-faint:#A8B0B8;
  --accent:#E8760C; --accent-strong:#FF8A1E; --accent-soft:rgba(232,118,12,.08);
  --good:#1E9E57; --warn:#B47B00; --bad:#D0362B; --info:#1B6FD1;
  --good-soft:rgba(30,158,87,.09); --warn-soft:rgba(180,123,0,.09); --bad-soft:rgba(208,54,43,.09);
  --mark-frame:var(--text); --mark-base:var(--accent);
  --header-bg:rgba(241,243,245,.92); --focus:#E8760C;
}

/* --- 3. Base ----------------------------------------------------------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--text-3);
  font:400 15px/1.62 var(--font-body);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
h1,h2,h3,h4{font-family:var(--font-display);font-weight:400;color:var(--text);margin:0;letter-spacing:.005em}
h1{font-size:44px;line-height:1.16}
h2{font-size:28px;line-height:1.28}
h3{font-size:20px;line-height:1.3}
h4{font-size:15px;line-height:1.34}
p{margin:0 0 var(--s4);text-wrap:pretty}
p:last-child{margin-bottom:0}
a{color:var(--accent);text-decoration:none}
a:hover{color:var(--accent-strong);text-decoration:underline}
strong{font-weight:700;color:var(--text-2)}
hr{border:0;border-top:var(--rule) solid var(--line);margin:var(--s7) 0}
/* ADDED FOR THE PORTAL. The kit styles no block-level code, no lists and no
   figures, and this app has all three: the Connect page and the panel both
   show copy-paste blocks, several pages number a process, and the landing
   page frames drawings of the desktop app. `overflow-x:auto` on <pre> is the
   load-bearing one — an unbroken bearer token in a code block pushes the
   whole page sideways on a phone without it. */
pre{margin:0 0 var(--s4);padding:var(--s5);background:var(--bg);
  border:var(--rule) solid var(--line-2);font:400 13px/1.6 var(--font-mono);
  color:var(--text-2);overflow-x:auto}
code{font:400 .92em/1.5 var(--font-mono);color:var(--text-2)}
pre code{font-size:1em;color:inherit}
ol,ul{margin:0 0 var(--s4);padding-left:var(--s6)}
li{margin:0 0 var(--s2)}
figure{margin:0}
:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
::selection{background:var(--accent);color:var(--on-accent)}

/* Screen-reader-only text (labels on icon-only controls) */
.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}

/* --- 4. Layout --------------------------------------------------------- */
.shell{max-width:var(--shell);margin:0 auto;padding:0 var(--s7)}
.shell--narrow{max-width:var(--shell-narrow)}
.section{padding:var(--s10) 0;border-bottom:var(--rule) solid var(--line)}
.section--flush{padding-top:var(--s8)}
.section--surface{background:var(--surface)}
.stack > * + *{margin-top:var(--s4)}
.stack-lg > * + *{margin-top:var(--s7)}
.row{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap}
.row--end{justify-content:flex-end}
.row--between{justify-content:space-between}
.spacer{margin-left:auto}
.grid{display:grid;gap:var(--s5)}
.grid--2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid--3{grid-template-columns:repeat(3,minmax(0,1fr))}
.grid--4{grid-template-columns:repeat(4,minmax(0,1fr))}
.grid--5{grid-template-columns:repeat(5,minmax(0,1fr))}
.grid--split{grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s9)}
@media (max-width:1080px){
  .grid--5,.grid--4{grid-template-columns:repeat(2,minmax(0,1fr))}
  .grid--split,.grid--3{grid-template-columns:minmax(0,1fr);gap:var(--s7)}
}
@media (max-width:680px){
  .shell{padding:0 var(--s5)}
  .grid--5,.grid--4,.grid--2{grid-template-columns:minmax(0,1fr)}
  h1{font-size:32px}h2{font-size:23px}
  .section{padding:var(--s8) 0}
}

/* --- 5. Eyebrow / label type ------------------------------------------ */
.eyebrow{font:500 11px/1 var(--font-mono);letter-spacing:.2em;color:var(--text-mute);text-transform:uppercase;margin:0 0 var(--s4)}
.eyebrow--accent{color:var(--accent)}
.label{font:500 10px/1 var(--font-mono);letter-spacing:.16em;color:var(--text-mute);text-transform:uppercase}
.lede{font:400 17px/1.6 var(--font-body);color:var(--text-dim);max-width:68ch}
.note{font:400 12.5px/1.6 var(--font-body);color:var(--text-mute)}
.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums}
.num{font-family:var(--font-display);font-variant-numeric:tabular-nums;color:var(--text)}

/* --- 6. Brand mark + nav ---------------------------------------------- */
/* Inline SVG in the Jinja layout; fills reference the mark tokens. Two
   planes on a 24x46 grid: a hollow frame (evenodd cuts the aperture) over a
   solid base. The mark is TALLER THAN IT IS WIDE, so the width is intrinsic
   and only the height is set — a square box would letterbox it.
   <svg class="mark" viewBox="0 0 24 46" fill="none" stroke="none" aria-hidden="true">
     <path class="mark__frame" fill-rule="evenodd" d="M0 0h24v34H0V0Zm6 6v22h12V6H6Z"/>
     <path class="mark__base" d="M9 34h6v4H9zM4 38h16v8H4z"/>
   </svg>                                                              */
.mark{width:auto;height:34px;display:block;flex:none}
.mark--sm{width:auto;height:26px}
.mark--lg{width:auto;height:96px}
.mark__frame{fill:var(--mark-frame)}
.mark__base{fill:var(--mark-base)}

.topbar{position:sticky;top:0;z-index:50;background:var(--header-bg);border-bottom:var(--rule) solid var(--line);backdrop-filter:blur(10px)}
.topbar__inner{max-width:var(--shell);margin:0 auto;padding:0 var(--s7);height:72px;display:flex;align-items:center;gap:var(--s7)}
.brand{display:flex;align-items:center;gap:var(--s3);text-decoration:none}
.brand:hover{text-decoration:none}
/* The lockup: mark, then ATLAS over "by GSM Deck". Atlas is the product,
   GSM Deck is the small print — so the product name carries the display
   face and the company sits under it in mono at a tenth the weight of
   attention. There is no separate product chip: the product IS the wordmark. */
.brand__stack{display:flex;flex-direction:column;gap:5px;align-items:flex-start}
.brand__word{font:400 17px/1 var(--font-display);letter-spacing:.10em;color:var(--text)}
.brand__word em{font-style:normal;color:var(--text-mute)}
.brand__by{font:400 10px/1 var(--font-mono);letter-spacing:.14em;color:var(--text-mute)}
.nav{display:flex;gap:var(--s1);margin-left:auto}
.nav__link{padding:9px 14px;font:500 13px/1 var(--font-body);color:var(--text-dim);border:var(--rule) solid transparent;text-decoration:none}
.nav__link:hover{color:var(--text);border-color:var(--line-2);text-decoration:none}
.nav__link[aria-current="page"]{color:var(--text);border-color:var(--line-2)}
.topbar__actions{display:flex;align-items:center;gap:var(--s3)}

/* Theme switch — a real form, works with JS off.
   POST /theme (form-action 'self'), sets a cookie, layout renders
   <html data-theme="...">. Three buttons: auto / light / dark. */
.themeswitch{display:flex;border:var(--rule) solid var(--line-2)}
.themeswitch__btn{appearance:none;border:0;background:transparent;cursor:pointer;
  padding:9px 11px;font:500 10px/1 var(--font-mono);letter-spacing:.1em;color:var(--text-mute)}
.themeswitch__btn:hover{color:var(--text-2)}
.themeswitch__btn[aria-pressed="true"]{background:var(--accent);color:var(--on-accent)}

/* --- 7. Buttons -------------------------------------------------------- */
.btn{
  display:inline-block;flex:none;white-space:nowrap;text-align:center;
  padding:14px 24px;border:var(--rule) solid var(--line-2);background:transparent;
  font:700 14px/1 var(--font-body);color:var(--text-2);
  text-decoration:none;cursor:pointer;appearance:none;
  /* 14 + 14 + 14 + 2 already comes to exactly 44px, so this changes nothing
     that renders. It is stated because a tap-target floor that exists only
     as an accident of the padding disappears the first time somebody tunes
     the padding — and because it is the only form of the guarantee a test
     can read. */
  min-height:44px;
}
.btn:hover{border-color:var(--text-mute);color:var(--text);text-decoration:none}
.btn--primary{
  border-color:transparent;background:var(--accent);color:var(--on-accent);
  clip-path:polygon(0 0,calc(100% - var(--chamfer)) 0,100% var(--chamfer),100% 100%,var(--chamfer) 100%,0 calc(100% - var(--chamfer)));
}
.btn--primary:hover{background:var(--accent-strong);color:var(--on-accent)}
.btn--danger{border-color:var(--bad);color:var(--bad)}
.btn--danger:hover{background:var(--bad-soft);border-color:var(--bad);color:var(--bad)}
/* Its own floor, matching its own padding (9+12.5+9+2), NOT the 44px above —
   otherwise the small variant inherits the big variant's height and stops
   being small. */
.btn--sm{padding:9px 14px;font-size:12.5px;min-height:33px}
.btn--block{display:block;width:100%}
.btn[disabled],.btn[aria-disabled="true"]{opacity:.45;cursor:not-allowed}
.btn-row{display:flex;gap:var(--s3);flex-wrap:wrap}

/* --- 8. Cards / panels ------------------------------------------------- */
.panel{background:var(--surface);border:var(--rule) solid var(--line-2)}
.panel__head{display:flex;align-items:center;gap:var(--s3);padding:var(--s5) var(--s6);border-bottom:var(--rule) solid var(--line)}
.panel__title{font:400 15px/1.3 var(--font-display);color:var(--text)}
.panel__body{padding:var(--s6)}
.panel__foot{padding:var(--s4) var(--s6);border-top:var(--rule) solid var(--line)}
.panel--accent{border-color:var(--accent)}

/* --- 9. Stat tiles ----------------------------------------------------- */
.stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--rule);background:var(--line)}
.stats--4{grid-template-columns:repeat(4,minmax(0,1fr))}
@media (max-width:820px){.stats,.stats--4{grid-template-columns:repeat(2,minmax(0,1fr))}}
.stat{background:var(--surface);padding:var(--s6)}
.stat__label{font:500 10px/1 var(--font-mono);letter-spacing:.16em;color:var(--text-mute);text-transform:uppercase;margin:0 0 var(--s3)}
.stat__value{font:400 30px/1 var(--font-display);color:var(--text);font-variant-numeric:tabular-nums}
.stat__meta{font:400 12.5px/1.5 var(--font-body);color:var(--text-mute);margin-top:var(--s3)}

/* Balance — the one number that matters. Amber, never decorative. */
.balance{background:var(--surface);border:var(--rule) solid var(--line-2);padding:var(--s7) var(--s7) var(--s6)}
.balance__label{font:500 10px/1 var(--font-mono);letter-spacing:.16em;color:var(--text-mute);text-transform:uppercase}
.balance__value{display:block;margin:var(--s4) 0 var(--s2);
  font:400 clamp(38px,6vw,58px)/1 var(--font-display);color:var(--accent);font-variant-numeric:tabular-nums}
.balance__meta{font:400 13.5px/1.5 var(--font-body);color:var(--text-dim)}
/* zero: drop the amber, state the fact */
.balance--zero .balance__value{color:var(--text-mute)}
/* negative: deliberate arrears, NOT an error. Steel number + warn rule. */
.balance--negative{border-color:var(--warn)}
.balance--negative .balance__value{color:var(--warn)}
.balance--negative .balance__meta{color:var(--text-2)}

/* --- 10. Status pills -------------------------------------------------- */
.pill{display:inline-flex;align-items:center;gap:var(--s2);
  padding:5px 8px;font:500 10px/1 var(--font-mono);letter-spacing:.12em;text-transform:uppercase;
  border:var(--rule) solid transparent}
.pill--live{background:var(--good);color:var(--on-accent)}
.pill--pending{background:var(--warn-soft);border-color:var(--warn);color:var(--warn)}
.pill--soon{background:var(--accent);color:var(--on-accent)}
.pill--off{background:transparent;border-color:var(--line-2);color:var(--text-mute)}
.pill--stop{background:var(--bad-soft);border-color:var(--bad);color:var(--bad)}
.pill--info{background:transparent;border-color:var(--info);color:var(--info)}
.pill__dot{width:6px;height:6px;background:currentColor;flex:none}

/* --- 11. Banners ------------------------------------------------------- */
.banner{display:flex;gap:var(--s4);align-items:flex-start;padding:var(--s5) var(--s6);
  border:var(--rule) solid var(--line-2);background:var(--surface);margin-bottom:var(--s6)}
.banner__bar{width:3px;align-self:stretch;flex:none;background:var(--text-mute)}
.banner__title{font:700 14.5px/1.3 var(--font-body);color:var(--text);margin:0 0 var(--s2)}
.banner__text{font:400 13.5px/1.55 var(--font-body);color:var(--text-dim);margin:0}
.banner--pending{border-color:var(--warn)}
.banner--pending .banner__bar{background:var(--warn)}
.banner--stop{border-color:var(--bad)}
.banner--stop .banner__bar{background:var(--bad)}
.banner--good{border-color:var(--good)}
.banner--good .banner__bar{background:var(--good)}
.banner--info .banner__bar{background:var(--info)}

/* --- 12. Pack grid (pricing) ------------------------------------------ */
.packs{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:var(--rule);background:var(--line)}
@media (max-width:1080px){.packs{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:560px){.packs{grid-template-columns:minmax(0,1fr)}}
.pack{background:var(--surface);padding:var(--s6);display:flex;flex-direction:column;gap:var(--s4)}
.pack--best{background:var(--surface-2);box-shadow:inset 0 3px 0 0 var(--accent)}
.pack__name{font:500 10px/1 var(--font-mono);letter-spacing:.16em;color:var(--text-mute);text-transform:uppercase}
.pack__price{font:400 26px/1 var(--font-display);color:var(--text);font-variant-numeric:tabular-nums}
.pack__lines{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s2)}
.pack__line{font:400 13px/1.4 var(--font-mono);color:var(--text-dim);font-variant-numeric:tabular-nums}
.pack__rate{font:500 13px/1 var(--font-mono);color:var(--accent);letter-spacing:.02em}
.pack__cta{margin-top:auto}
.pack__tag{font:500 10px/1 var(--font-mono);letter-spacing:.14em;color:var(--accent);text-transform:uppercase}

/* --- 13. Tables (pricing table + ledger) ------------------------------ */
.table-wrap{border:var(--rule) solid var(--line-2);background:var(--surface);overflow-x:auto}
.table{width:100%;border-collapse:collapse;font:400 14px/1.5 var(--font-body)}
.table caption{text-align:left;padding:var(--s5) var(--s6);border-bottom:var(--rule) solid var(--line);
  font:400 15px/1.3 var(--font-display);color:var(--text)}
.table th{
  text-align:left;padding:var(--s4) var(--s5);border-bottom:var(--rule) solid var(--line);
  font:500 10px/1 var(--font-mono);letter-spacing:.14em;color:var(--text-mute);text-transform:uppercase;white-space:nowrap}
.table td{padding:var(--s4) var(--s5);border-bottom:var(--rule) solid var(--line-3);color:var(--text-3);vertical-align:top}
.table tbody tr:last-child td{border-bottom:0}
.table tbody tr:hover td{background:var(--surface-2)}
.table--zebra tbody tr:nth-child(even) td{background:var(--surface-2)}
.table__num{text-align:right;font-family:var(--font-mono);font-variant-numeric:tabular-nums;white-space:nowrap}
.table__strong{color:var(--text);font-weight:700}
.table__dim{color:var(--text-mute)}
.table__row--highlight td{background:var(--accent-soft)}
/* sticky header for a 200-row ledger */
.table-wrap--tall{max-height:640px;overflow-y:auto}
.table-wrap--tall .table th{position:sticky;top:0;background:var(--surface);z-index:1}

/* signed amounts — colour AND a sign, never colour alone */
.amt{font-family:var(--font-mono);font-variant-numeric:tabular-nums;white-space:nowrap}
.amt--pos{color:var(--good)}
.amt--neg{color:var(--bad)}
.amt--zero{color:var(--text-mute)}
.amt--running{color:var(--text);font-weight:500}

/* Stack the ledger on small screens: give each td a data-label in Jinja */
@media (max-width:680px){
  .table--stack thead{display:none}
  .table--stack tr{display:block;border-bottom:var(--rule) solid var(--line);padding:var(--s3) 0}
  .table--stack td{display:flex;justify-content:space-between;gap:var(--s4);border:0;padding:var(--s2) var(--s5);text-align:right}
  .table--stack td::before{content:attr(data-label);font:500 10px/1.4 var(--font-mono);letter-spacing:.12em;
    color:var(--text-mute);text-transform:uppercase;text-align:left}
}

/* --- 14. Pagination (200-row ledger) ---------------------------------- */
.pager{display:flex;align-items:center;gap:var(--s2);padding:var(--s4) var(--s6);border-top:var(--rule) solid var(--line)}
.pager__info{font:400 12.5px/1 var(--font-mono);color:var(--text-mute);margin-right:auto}
.pager__link{padding:8px 12px;border:var(--rule) solid var(--line-2);font:500 12px/1 var(--font-mono);color:var(--text-3);text-decoration:none}
.pager__link:hover{border-color:var(--text-mute);color:var(--text);text-decoration:none}
.pager__link[aria-current="page"]{background:var(--accent);border-color:transparent;color:var(--on-accent)}
.pager__link[aria-disabled="true"]{opacity:.4;pointer-events:none}

/* --- 15. Forms -------------------------------------------------------- */
.form{display:flex;flex-direction:column;gap:var(--s5)}
.field{display:flex;flex-direction:column;gap:var(--s2)}
.field__label{font:500 11px/1 var(--font-mono);letter-spacing:.14em;color:var(--text-dim);text-transform:uppercase}
.field__input,.field__select,.field__textarea{
  width:100%;padding:13px 14px;background:var(--bg);color:var(--text);
  border:var(--rule) solid var(--line-2);border-radius:var(--radius);
  font:400 15px/1.3 var(--font-body);appearance:none}
.field__input:hover,.field__select:hover{border-color:var(--text-mute)}
.field__input:focus,.field__select:focus,.field__textarea:focus{outline:2px solid var(--focus);outline-offset:0;border-color:var(--focus)}
.field__input::placeholder{color:var(--text-faint)}
.field__input[type="text"].mono,.field__input--mono{font-family:var(--font-mono);letter-spacing:.04em}
.field__help{font:400 12.5px/1.5 var(--font-body);color:var(--text-mute)}
.field__error{font:500 12.5px/1.5 var(--font-body);color:var(--bad)}
.field--error .field__input,.field--error .field__select{border-color:var(--bad)}
.field--error .field__label{color:var(--bad)}
.check{display:flex;gap:var(--s3);align-items:flex-start;font:400 13.5px/1.5 var(--font-body);color:var(--text-3)}
.check input{margin-top:3px;accent-color:var(--accent)}
.form__foot{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap}
.form__aside{font:400 13.5px/1.5 var(--font-body);color:var(--text-mute)}

/* Auth card — login / signup / reset all use this */
.authcard{max-width:var(--shell-narrow);margin:var(--s10) auto;background:var(--surface);border:var(--rule) solid var(--line-2)}
.authcard__head{padding:var(--s7) var(--s7) var(--s5);border-bottom:var(--rule) solid var(--line)}
.authcard__title{font:400 22px/1.24 var(--font-display);color:var(--text);margin:0 0 var(--s3)}
.authcard__sub{font:400 14px/1.55 var(--font-body);color:var(--text-dim);margin:0}
.authcard__body{padding:var(--s7)}
.authcard__foot{padding:var(--s5) var(--s7);border-top:var(--rule) solid var(--line);
  font:400 13.5px/1.5 var(--font-body);color:var(--text-mute)}

/* --- 16. API key rows ------------------------------------------------- */
.keys{border:var(--rule) solid var(--line-2);background:var(--surface)}
.keyrow{display:flex;align-items:center;gap:var(--s4);padding:var(--s5) var(--s6);border-bottom:var(--rule) solid var(--line-3);flex-wrap:wrap}
.keyrow:last-child{border-bottom:0}
.keyrow__main{min-width:260px;flex:1}
.keyrow__name{font:700 14px/1.3 var(--font-body);color:var(--text);margin:0 0 var(--s2)}
.keyrow__secret{font:400 13px/1.4 var(--font-mono);color:var(--text-dim);letter-spacing:.04em;word-break:break-all}
.keyrow__meta{font:400 12px/1.4 var(--font-mono);color:var(--text-mute);margin-top:var(--s2)}
.keyrow__actions{display:flex;gap:var(--s2);flex-wrap:wrap}
.keyrow--revoked .keyrow__name,.keyrow--revoked .keyrow__secret{color:var(--text-faint);text-decoration:line-through}
/* Reveal without JS: server renders masked by default, a form POST
   re-renders the row unmasked for that request only. Copy is a
   <button type="submit"> that returns the key in a readonly input. */
.keyrow__reveal{display:flex;gap:var(--s2);align-items:center}

/* --- 17. Empty states ------------------------------------------------- */
.empty{border:var(--rule) dashed var(--line-2);padding:var(--s9) var(--s7);text-align:center}
.empty__mark{width:56px;height:56px;margin:0 auto var(--s5);opacity:.5}
.empty__title{font:400 17px/1.3 var(--font-display);color:var(--text-2);margin:0 0 var(--s3)}
.empty__text{font:400 14px/1.6 var(--font-body);color:var(--text-mute);margin:0 auto var(--s5);max-width:46ch}
.empty--tight{padding:var(--s7) var(--s6)}

/* --- 18. Steps / process --------------------------------------------- */
.steps{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:var(--s4)}
@media (max-width:1080px){.steps{grid-template-columns:repeat(2,minmax(0,1fr))}}
.step{border-top:3px solid var(--line-2);padding:var(--s5) var(--s1) 0}
.step--first{border-top-color:var(--accent)}
.step__n{font:500 10px/1 var(--font-mono);letter-spacing:.16em;color:var(--text-mute);margin:0 0 var(--s3)}
.step__title{font:700 15px/1.3 var(--font-body);color:var(--text);margin:0 0 var(--s2)}
.step__text{font:400 13.5px/1.55 var(--font-body);color:var(--text-dim);margin:0}

/* --- 19. Feature / claim list ---------------------------------------- */
.claims{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s3)}
.claim{display:flex;gap:var(--s3);align-items:flex-start;font:400 14px/1.55 var(--font-body);color:var(--text-3)}
.claim::before{content:"";width:6px;height:6px;background:var(--accent);margin-top:8px;flex:none}
.rule-list{display:flex;flex-direction:column;gap:var(--s5)}
.rule-item{border-left:2px solid var(--line-2);padding:2px 0 2px var(--s5)}
.rule-item--accent{border-left-color:var(--accent)}
.rule-item__title{font:700 14.5px/1.3 var(--font-body);color:var(--text);margin:0 0 var(--s2)}
.rule-item__text{font:400 14px/1.6 var(--font-body);color:var(--text-dim);margin:0}

/* --- 20. Admin (denser) ---------------------------------------------- */
.admin{--s6:16px}
.admin .table td{padding:10px var(--s4);font-size:13px}
.admin .table th{padding:10px var(--s4)}
.admin .panel__head{padding:var(--s4) var(--s5)}
.admin .stat{padding:var(--s5)}
.admin .stat__value{font-size:24px}
.adminbar{display:flex;align-items:center;gap:var(--s3);padding:var(--s3) var(--s7);
  background:var(--surface-2);border-bottom:var(--rule) solid var(--line)}
.adminbar__tag{font:500 10px/1 var(--font-mono);letter-spacing:.16em;color:var(--warn);text-transform:uppercase}
.filters{display:flex;gap:var(--s2);flex-wrap:wrap;padding:var(--s4) var(--s6);border-bottom:var(--rule) solid var(--line)}
.filters .field__input,.filters .field__select{padding:9px 11px;font-size:13px;width:auto}

/* --- 20b. Meters (server-rendered inline SVG) -------------------------
   <svg class="meter" viewBox="0 0 100 4" role="img" aria-label="58% used">
     <rect class="meter__track" width="100" height="4"/>
     <rect class="meter__fill"  width="58"  height="4"/>
   </svg>
   Width is rendered by Jinja. No chart library, no JS.
-------------------------------------------------------------------------- */
.meter{display:block;width:100%;height:4px}
.meter--tall{height:8px}
.meter__track{fill:var(--line-2)}
.meter__fill{fill:var(--accent)}
.meter--good .meter__fill{fill:var(--good)}
.meter--warn .meter__fill{fill:var(--warn)}
.meter--bad .meter__fill{fill:var(--bad)}
.meter__label{font:500 10px/1 var(--font-mono);letter-spacing:.14em;color:var(--text-mute);text-transform:uppercase;margin-bottom:var(--s2)}

/* --- 20c. Charts and diagrams (server-rendered inline SVG) ------------
   ADDED FOR THE PORTAL. The kit shipped meters but no chart rules, and this
   app puts three kinds of generated SVG on its pages:

     atlas_api/charts.py    the usage bar chart and the spend-cap meter
     atlas_api/mockups.py   the how-it-works flow diagram and the app drawings

   The chart carries NO fill or stroke attributes of its own — every colour
   comes from here. That is not a style choice, it is the only option: with
   `style-src 'self'` the generator cannot emit style="", and with no
   script-src there is no library to do it either. So a missing rule in this
   section is not an unstyled chart, it is an INVISIBLE one — SVG's default
   fill is black, on a #0F1114 page. Removing a rule here removes a chart.

   The diagrams are the other way round: mockups.py paints them with
   presentation attributes, so they only need sizing.
-------------------------------------------------------------------------- */
.chart-wrap{margin:var(--s4) 0 var(--s2)}
.chart{display:block;width:100%;height:auto}
.chart__bar{fill:var(--accent)}
.chart__axis{stroke:var(--line-2);stroke-width:1}
.chart__label,.chart__peak{fill:var(--text-mute);font:400 13px/1 var(--font-mono)}
.chart__empty{fill:var(--text-mute);font:400 15px/1 var(--font-body)}

.flow,.mockup{display:block;width:100%;height:auto}
.flow{overflow:visible}
.shots{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s6)}
@media (max-width:820px){.shots{grid-template-columns:minmax(0,1fr)}}
.shot{margin:0}
/* THE SLOT RESERVES ITS SHAPE. aspect-ratio holds the box at 16:10 whatever
   goes in it, and overflow:hidden keeps a child that does not fit from
   spilling. Dropping these two in the design-system port was a regression
   that nothing caught: today's occupants are the SVG drawings from
   mockups.py, which are 640x400 and therefore already 16:10, so the geometry
   only diverges the day a real PNG replaces one. Then the slot stops holding
   the shape, the image sets its own height, and the two-column .shots grid
   goes ragged with no error anywhere.
   With height:100% on the child, an <svg> with a viewBox letterboxes itself;
   an <img> is STRETCHED to fill the slot. Cropping would need
   object-fit:cover, which is deliberately not here — docs/SCREENSHOTS.md
   says crop to 16:10 for exactly this reason. */
.shot__frame{border:var(--rule) solid var(--line);background:var(--surface-2);
  aspect-ratio:16/10;overflow:hidden}
.shot__frame>svg,.shot__frame>img{display:block;width:100%;height:100%}
.shot__caption{font:400 12.5px/1.6 var(--font-body);color:var(--text-mute);margin-top:var(--s3)}

/* --- 21. Footer ------------------------------------------------------- */
.footer{background:var(--surface);border-top:var(--rule) solid var(--line)}
.footer__inner{max-width:var(--shell);margin:0 auto;padding:var(--s9) var(--s7) var(--s8);
  display:grid;grid-template-columns:1.6fr 1fr 1fr;gap:var(--s8)}
@media (max-width:820px){.footer__inner{grid-template-columns:minmax(0,1fr);gap:var(--s7)}}
.footer__blurb{font:400 13.5px/1.6 var(--font-body);color:var(--text-mute);max-width:34ch;margin:var(--s4) 0 0}
.footer__col{display:flex;flex-direction:column;gap:var(--s3)}
.footer__link{font:500 13.5px/1 var(--font-body);color:var(--text-3);text-decoration:none}
.footer__link:hover{color:var(--accent);text-decoration:none}
.footer__base{max-width:var(--shell);margin:0 auto;padding:0 var(--s7) var(--s8)}
.footer__rule{border-top:var(--rule) solid var(--line);padding-top:var(--s5);display:flex;gap:var(--s5);flex-wrap:wrap;
  font:400 12px/1 var(--font-mono);letter-spacing:.08em;color:var(--text-faint)}

/* --- 22. Print (invoices / receipts) --------------------------------- */
@media print{
  :root{--bg:#fff;--surface:#fff;--text:#000;--text-2:#111;--text-3:#222;
        --text-dim:#444;--text-mute:#666;--line:#ccc;--line-2:#999;--line-3:#e5e5e5;
        --accent:#000;--mark-frame:#000;--mark-base:#666}
  .topbar,.nav,.themeswitch,.btn,.pager,.footer__col{display:none}
  .panel,.table-wrap{border-color:#999}
  .table-wrap--tall{max-height:none;overflow:visible}
  body{font-size:11pt}
}

/* --- 23. Motion ------------------------------------------------------- */
@media (prefers-reduced-motion:no-preference){
  .btn,.nav__link,.footer__link,.pager__link{transition:background-color .12s linear,border-color .12s linear,color .12s linear}
}
