/* The customer portal.
 *
 * The prototype is no longer in the tree — this file and internal/portal's
 * templates are the reference now. Every citation of it here and there is a git
 * object: `git show 14ba1e8:web/dashboard/styles.css`.
 *
 * This is 14ba1e8:web/dashboard/styles.css rebuilt on the shared tokens in aifu.css:
 * --cobalt became var(--brand), --cobalt-dark var(--brand-ink), --text and the
 * text uses of --navy var(--ink), --font-sans/--font-mono var(--sans)/
 * var(--mono), and --surface --border --ok --ok-bg --warn --warn-bg --err
 * --err-bg --info --info-bg --coral now live in aifu.css rather than here. The
 * design's class names are kept verbatim, so the prototype and the served
 * portal diff cleanly and a rule can be traced back to the file it came from.
 *
 * One split the brief did not foresee: the design's --navy is two things, a
 * text colour and the fixed dark surface under the sidebar, the code blocks,
 * the dialog headers and the avatar. Its dark theme flips --text and leaves
 * --navy alone, so mapping both onto --ink would have turned the sidebar
 * near-white the moment B(d) wires the toggle. The surfaces use --bg-dark
 * instead, which aifu.css already defines as the same #071B4A and which does
 * not flip.
 *
 * Loaded AFTER aifu.css, and that order matters: aifu.css is not purely
 * prefixed — it styles html, body, a, img and h1/h2/h3 globally at landing
 * sizes (aifu.css:64-89), which on a dashboard makes every card heading a
 * 50px hero. The block below re-declares exactly those elements with the
 * design's values; the headings are then scoped the way the design scopes
 * them, under .page-head, .card and .auth-card.
 *
 * Dropped from the prototype: .screen/.active/body.auth-mode (the hash
 * router's artefacts — every page here is its own URL) and .sample-flag
 * (there is no sample data). The html.dark rules and .theme-toggle were
 * carried unused through B(b) and B(c) and are live from B(d) on: the button
 * in the topbar is the one thing the port was missing.
 */

/* ---- resets aifu.css would otherwise win --------------------------------- */

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:auto;scroll-padding-top:0}
html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:16px;line-height:1.6}
img{max-width:100%}
a{color:var(--brand-ink);text-decoration:underline}
a:hover{color:var(--ink)}
h1,h2,h3{font-size:inherit;font-weight:600;line-height:1.3;margin:0}
/* The focus ring is the design's, and it has to out-specify aifu.css's
   a:focus-visible / button:focus-visible (0,1,1) — hence the descendant. */
body :focus-visible{outline:3px solid var(--sky);outline-offset:2px}

/* ---- utilities ----------------------------------------------------------- */

.skip-link{position:absolute;left:-9999px;top:0;background:var(--bg-dark);color:#fff;padding:12px 18px;z-index:300;font-weight:600}
.skip-link:focus{left:12px;top:12px}
.num{font-variant-numeric:tabular-nums}
.mono{font-family:var(--mono);font-size:0.94em}
.muted{color:var(--muted)}
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ---- shell --------------------------------------------------------------- */

.app{display:grid;grid-template-columns:240px minmax(0,1fr);min-height:100vh}
.sidebar{background:var(--bg-dark);color:#DCE7F7;display:flex;flex-direction:column}
.brand{background:#fff;border-bottom:1px solid var(--border);padding:16px 18px;display:flex;align-items:center;justify-content:space-between;gap:10px}
.brand img{height:40px;width:auto;display:block}
.brand .env{color:var(--brand-ink);font-weight:700;letter-spacing:0.14em;font-size:11.5px;border:1px solid rgba(8,104,242,0.35);padding:4px 9px;background:var(--info-bg);font-family:var(--mono)}
.nav{display:flex;flex-direction:column;padding:12px 0}
.nav a{display:flex;align-items:center;gap:12px;min-height:50px;padding:0 20px;color:#C4D4F0;text-decoration:none;border-left:3px solid transparent;font-weight:500;font-size:15.5px}
.nav a:hover{background:rgba(67,185,255,0.12);color:#fff}
.nav a[aria-current="page"]{background:rgba(8,104,242,0.28);border-left-color:var(--sky);color:#fff}
.nav a svg{flex:none;color:#8FB3E8}
.nav a[aria-current="page"] svg{color:var(--sky)}
.sidebar .foot{margin-top:auto;padding:14px 18px;border-top:1px solid rgba(220,231,247,0.14);display:flex;flex-direction:column;gap:6px;font-size:13px;color:#8FB3E8}
/* The design carried this column as an inline style on an anonymous div. */
.shell-col{min-width:0;display:flex;flex-direction:column}

/* ---- topbar -------------------------------------------------------------- */

.topbar{position:sticky;top:0;z-index:70;background:var(--surface);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;padding:10px clamp(14px,2.5vw,28px);min-height:62px}
.menu-btn{display:none;min-width:44px;min-height:44px;background:transparent;border:1px solid var(--border);color:var(--ink);cursor:pointer;align-items:center;justify-content:center}
.topbar .plan-chip{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--border);background:var(--bg);padding:8px 12px;font-size:13.5px}
.topbar .plan-chip b{font-family:var(--mono)}
/* The prototype pushed its sample-data flag to the right edge and the theme
   toggle sat immediately after it; here the toggle is itself the first thing
   in the right-hand group, and the gap opens in front of it. */
.topbar .theme-toggle{margin-left:auto}
.lang-switch{display:inline-flex;border:1px solid var(--border)}
/* Two links rather than two buttons: the choice is a URL, so it is navigable,
   bookmarkable and works before any script has run. */
.lang-switch a{display:inline-flex;align-items:center;justify-content:center;min-width:48px;min-height:44px;background:#fff;color:var(--muted);text-decoration:none;font-family:var(--mono);font-size:13px;letter-spacing:0.08em}
.lang-switch a[aria-pressed="true"]{background:var(--brand);color:#fff}
.user-menu{position:relative}
.user-menu>summary{list-style:none;display:flex;align-items:center;gap:10px;min-height:44px;padding:0 10px;border:1px solid var(--border);cursor:pointer;background:#fff}
.user-menu>summary::-webkit-details-marker{display:none}
.user-menu .avatar{width:32px;height:32px;border-radius:50%;background:var(--bg-dark);color:#fff;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;flex:none}
.user-menu .menu{position:absolute;right:0;top:calc(100% + 6px);background:#fff;border:1px solid var(--border);box-shadow:0 12px 28px rgba(7,27,74,0.14);min-width:220px;display:flex;flex-direction:column;z-index:90}
.user-menu .menu a,.user-menu .menu button{display:flex;align-items:center;min-height:46px;padding:0 16px;font:inherit;font-size:14.5px;color:var(--ink);text-decoration:none;background:#fff;border:0;cursor:pointer;text-align:left;border-bottom:1px solid var(--border);width:100%}
.user-menu .menu a:hover,.user-menu .menu button:hover{background:var(--bg)}
.user-menu .menu .who{padding:12px 16px;border-bottom:1px solid var(--border);font-size:13px;color:var(--muted)}

/* ---- main ---------------------------------------------------------------- */

.main{min-width:0;padding:clamp(18px,3vw,34px);display:flex;flex-direction:column;gap:18px;max-width:1160px;width:100%}
.page-head{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap}
.page-head h1{margin:0;font-size:clamp(23px,2.4vw,29px);line-height:1.2}
.page-head .sub{color:var(--muted);font-size:14.5px}
.page-actions{margin-left:auto;display:flex;gap:10px;flex-wrap:wrap}
.card{background:var(--surface);border:1px solid var(--border);padding:22px}
.card h2,.card h3{margin:0 0 8px;font-size:17.5px}
.card .lead{margin:0 0 14px;color:var(--muted);font-size:14.5px}
.grid{display:grid;gap:16px}
/* The design set each grid's tracks inline. Two widths cover every page. */
.grid.cols-2{grid-template-columns:repeat(auto-fit,minmax(min(100%,380px),1fr))}
.grid.cols-3{grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr))}
.band{background:var(--bg-alt);border:1px solid var(--border);padding:14px 16px;font-size:14.5px;margin:0}
hr.sep{border:0;border-top:1px solid var(--border);margin:16px 0}
/* A card whose heading sits in its own bordered strip: the card loses its
   padding and the strip carries it. */
.card.flush{padding:0}
.card-head{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border)}
.card-head h2{margin:0}
.card-head .spacer{margin-left:auto;font-size:14px}
.card-body{padding:14px 20px;font-size:14px}
.row-actions{display:flex;gap:8px;flex-wrap:wrap}
/* The design wrote the usage page's filter row and the billing page's plan
   strip as inline flex rows on anonymous divs. */
.filters{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
.facts{display:flex;gap:clamp(18px,4vw,44px);flex-wrap:wrap;align-items:baseline}
.facts>div{display:flex;flex-direction:column;gap:2px}
.facts .k{font-size:13px;color:var(--muted)}
.facts .v{font-size:24px;font-weight:700}
.facts .v small{font-size:13px;font-weight:400;color:var(--muted)}
/* A card whose body is one figure — the cache share on /portal/usage. The
   credit hero has its own, larger .big; this is the card-sized one. */
.card .big{font-family:var(--mono);font-size:36px;font-weight:700;line-height:1.1;color:var(--ink)}
/* A <details> card: the design styled its summary inline. */
.card>summary{cursor:pointer;font-weight:600;font-size:17.5px;min-height:44px;display:flex;align-items:center}

/* ---- buttons ------------------------------------------------------------- */

.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:0 18px;font:inherit;font-weight:600;font-size:14.5px;cursor:pointer;border:1px solid transparent;text-decoration:none}
.btn.primary{background:var(--brand);color:#fff}
.btn.primary:hover{background:var(--brand-ink);color:#fff}
.btn.ghost{background:transparent;color:var(--brand-ink);border-color:var(--brand)}
.btn.ghost:hover{background:var(--info-bg)}
.btn.quiet{background:#fff;color:var(--muted);border-color:var(--border)}
.btn.quiet:hover{color:var(--ink);border-color:var(--muted)}
.btn.danger{background:var(--err);color:#fff}
.btn.danger:hover{background:#8F1D17;color:#fff}
.btn[disabled],.btn[aria-disabled="true"]{opacity:0.55;cursor:not-allowed}
.btn.small{min-height:38px;padding:0 12px;font-size:13.5px}
/* A copy button swaps its own label — portal.js writes "Copied" over "Copy key"
   and puts it back — so the button changes width while it is being pressed and
   shoves whatever is beside it, which on the reveal page is the key. The floor
   clears the widest of the four labels in either language plus this button's
   padding, so no swap moves anything. */
.btn[data-copy]{min-width:8em;text-align:center}
/* The design's full-width auth buttons, which it wrote inline. */
.btn.block{width:100%}
/* A submit that spends 600,000 PBKDF2 iterations before the page changes
   looked frozen with a clickable button on it. Mirrors aifu.css's
   .aifu-btn[aria-busy]; portal.js sets the attribute, .portal-busy is the
   same state for anything that is not a .btn. */
.btn[aria-busy="true"],.portal-busy{opacity:.85;cursor:progress;position:relative;padding-right:46px}
.btn[aria-busy="true"]::after,.portal-busy::after{
  content:"";position:absolute;right:18px;top:50%;width:14px;height:14px;margin-top:-7px;
  border:2px solid rgba(255,255,255,0.4);border-top-color:#fff;border-radius:50%;
  animation:portal-spin 700ms linear infinite;
}
@keyframes portal-spin{to{transform:rotate(360deg)}}

/* ---- badges -------------------------------------------------------------- */

.badge{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;padding:3px 9px;border:1px solid;white-space:nowrap}
.badge::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}
.badge.ok{color:var(--ok);background:var(--ok-bg);border-color:rgba(11,107,88,0.4)}
.badge.solid-ok{background:#0F9D6E;color:#fff;border-color:#0F9D6E}
.badge.warn{color:var(--warn);background:var(--warn-bg);border-color:rgba(168,65,15,0.4)}
.badge.err{color:var(--err);background:var(--err-bg);border-color:rgba(179,38,30,0.4)}
.badge.info{color:var(--info);background:var(--info-bg);border-color:rgba(10,87,200,0.35)}
.badge.neutral{color:var(--muted);background:#EDF1F8;border-color:rgba(93,109,139,0.4)}

/* ---- forms --------------------------------------------------------------- */

.field{display:flex;flex-direction:column;gap:6px;margin-bottom:14px}
.field label{font-weight:600;font-size:14.5px}
/* Not .field .hint any more: the same small grey line is now the note under the
   key box on the reveal page, which is not inside a field. Same declarations,
   one selector. */
.hint{font-size:13px;color:var(--muted)}
.field input,.field select,.field textarea{font:inherit;padding:10px 12px;border:1px solid var(--border);background:#fff;color:var(--ink);min-height:44px}
.field input:focus,.field select:focus{outline:3px solid var(--sky);outline-offset:0;border-color:var(--brand)}

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

.table-wrap{overflow-x:auto;background:var(--surface);border:1px solid var(--border)}
.table-wrap.flush{border:0}
table{border-collapse:collapse;width:100%;font-size:14.5px}
caption{text-align:left;padding:12px 16px;font-weight:600;border-bottom:1px solid var(--border)}
th,td{text-align:left;padding:11px 14px;border-bottom:1px solid var(--border);vertical-align:middle}
th{font-size:12px;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);background:var(--bg-alt);white-space:nowrap}
tbody tr:hover{background:#FBFDFF}
td.r,th.r{text-align:right}

/* ---- credit meter -------------------------------------------------------- */

.credit-hero{background:var(--surface);border:1px solid var(--border);border-top:3px solid var(--brand);padding:clamp(22px,3vw,32px);display:grid;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);gap:clamp(18px,3vw,36px)}
.credit-hero .label{font-size:13px;letter-spacing:0.1em;text-transform:uppercase;color:var(--muted)}
.credit-hero .big{font-size:clamp(38px,4.6vw,54px);font-weight:700;line-height:1.05;color:var(--ink);font-family:var(--mono)}
.credit-hero .big small{font-size:0.42em;font-weight:600;color:var(--muted)}
.meter{height:16px;background:var(--bg-alt);border:1px solid var(--border);position:relative;margin:14px 0 8px}
.meter>span{position:absolute;top:0;bottom:0;left:0;background:linear-gradient(90deg,var(--brand),var(--sky))}
.credit-hero .side{display:flex;flex-direction:column;gap:12px;border-left:1px solid var(--border);padding-left:clamp(16px,2.5vw,30px)}
.credit-hero .side .row{display:flex;justify-content:space-between;gap:12px;font-size:14.5px;border-bottom:1px solid var(--border);padding-bottom:10px}
.credit-hero .side .row:last-of-type{border-bottom:0}
.credit-hero .side .row b{font-family:var(--mono)}
.credit-hero .foot{font-size:14px;color:var(--muted)}

/* ---- model usage rows ---------------------------------------------------- */

.model-rows{display:flex;flex-direction:column;gap:14px}
.model-rows .row{display:grid;grid-template-columns:64px 1fr auto;gap:14px;align-items:center}
.model-rows .alias{font-family:var(--mono);font-weight:600}
.model-rows .track{height:12px;background:var(--bg-alt);border:1px solid var(--border);position:relative;cursor:help;transition:border-color 150ms ease}
/* The prototype set a colour per model inline, out of a fixed palette of three.
   The server draws whatever aliases were actually called, so the fill is one
   rule rather than a colour the markup has to carry. */
.model-rows .track>span{position:absolute;top:0;bottom:0;left:0;background:linear-gradient(90deg,var(--brand),var(--sky))}
.model-rows .row:hover .track{border-color:var(--brand)}
.model-rows .val{font-family:var(--mono);font-size:14px;white-space:nowrap}

/* ---- charts -------------------------------------------------------------- */

.chart{position:relative}
.chart svg{width:100%;height:auto;display:block}
.chart .tick{font:11px var(--mono);fill:var(--muted)}
/* The prototype passed the bar colour into its SVG generator. Server-rendered
   markup carries no colours, so the unit the page is drawn in is an attribute
   and the fill is a rule: cobalt for credits, sky for raw tokens, as the
   design's two calls did. */
.chart rect.bar{fill:var(--brand);transition:opacity 120ms ease}
.chart[data-unit="raw"] rect.bar{fill:var(--sky)}
.chart.hovering rect.bar{opacity:0.42}
.chart rect.bar.active{opacity:1}
.chart rect.hit{cursor:crosshair}
.chart-tip{position:absolute;transform:translateX(-50%);background:var(--bg-dark);color:#fff;border:1px solid var(--sky);padding:8px 12px;font-size:13px;line-height:1.45;pointer-events:none;z-index:20;white-space:nowrap;display:flex;flex-direction:column;gap:2px;box-shadow:0 8px 20px rgba(7,27,74,0.35)}
.chart-tip b{font-size:12px;letter-spacing:0.08em;text-transform:uppercase;color:var(--mint)}
.chart-tip span{font-family:var(--mono);font-variant-numeric:tabular-nums}
.legend{display:flex;gap:16px;flex-wrap:wrap;font-size:13.5px}
.legend span{display:inline-flex;align-items:center;gap:7px}
.legend i{width:11px;height:11px;display:inline-block}

/* ---- tabs and code ------------------------------------------------------- */

.tabs{display:flex;border-bottom:2px solid var(--border);flex-wrap:wrap}
.tabs button{font:inherit;font-weight:600;font-size:14.5px;background:transparent;border:0;border-bottom:3px solid transparent;margin-bottom:-2px;padding:10px 18px;min-height:44px;color:var(--muted);cursor:pointer}
.tabs button[aria-selected="true"]{color:var(--ink);border-bottom-color:var(--brand)}
pre.code{margin:0;padding:18px;background:var(--bg-dark);color:#CFE3FF;font-family:var(--mono);font-size:13.5px;line-height:1.7;overflow-x:auto}
pre.code .c1{color:var(--mint)}
pre.code .c2{color:#FFC59E}
.code-head{display:flex;align-items:center;gap:12px;padding:10px 14px;border:1px solid var(--border);border-bottom:0;background:var(--surface);font-family:var(--mono);font-size:13px;color:var(--muted)}
.code-head.flush{border:0;border-bottom:1px solid var(--border)}
.code-head .btn{margin-left:auto}

/* ---- switcher chips ------------------------------------------------------ */

.switcher{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.switcher .lbl{font-size:12px;letter-spacing:0.1em;text-transform:uppercase;color:var(--muted)}
/* The prototype's chips were buttons a script listened to. Here each chip is a
   URL the server re-renders, for the reason .lang-switch is: the choice is
   navigable, bookmarkable and works before any script has run. The anchor is
   given the button's own rule rather than a second one. */
.switcher button,.switcher a{min-height:38px;padding:0 12px;font:inherit;font-size:13.5px;background:#fff;border:1px solid var(--border);color:var(--muted);cursor:pointer}
.switcher a{display:inline-flex;align-items:center;text-decoration:none}
.switcher button[aria-pressed="true"],.switcher a[aria-pressed="true"]{background:var(--bg-dark);border-color:var(--bg-dark);color:#fff}

/* ---- dialogs ------------------------------------------------------------- */

dialog{border:1px solid var(--border);padding:0;max-width:540px;width:calc(100vw - 32px)}
dialog::backdrop{background:rgba(7,27,74,0.55)}
.dlg-head{background:var(--bg-dark);color:#fff;padding:14px 18px;font-weight:600;display:flex;justify-content:space-between;gap:12px;align-items:center}
.dlg-head.danger{background:var(--err)}
.dlg-head .btn.quiet{color:#fff;background:transparent;border-color:rgba(255,255,255,0.5)}
.dlg-body{padding:20px;display:flex;flex-direction:column;gap:14px}
.dlg-foot{padding:14px 18px;border-top:1px solid var(--border);display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap}
.key-reveal{display:flex;gap:10px;flex-wrap:wrap;align-items:stretch}
.key-reveal code{flex:1;min-width:200px;display:flex;align-items:center;padding:12px 14px;background:var(--bg-dark);color:#BFE3FF;font-family:var(--mono);font-size:13.5px;overflow-wrap:anywhere}
.check-line{display:flex;gap:10px;align-items:flex-start;font-size:14.5px}
.check-line input{width:20px;height:20px;margin-top:2px}

/* ---- states -------------------------------------------------------------- */

.empty-state{text-align:center;padding:46px 20px;color:var(--muted)}
.empty-state h2{margin:0 0 8px}
.error-state{border:1px solid rgba(179,38,30,0.4);background:var(--err-bg);color:var(--err);padding:16px 18px;display:flex;gap:12px;align-items:flex-start}
.loading-row{display:flex;flex-direction:column;gap:10px;padding:20px}
.skl{height:14px;background:linear-gradient(90deg,var(--bg-alt),#F2F7FF,var(--bg-alt));background-size:200% 100%;animation:skl 1.4s linear infinite}
@keyframes skl{to{background-position:-200% 0}}

/* ---- plans --------------------------------------------------------------- */

.plan-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:16px}
.plan-card{background:var(--surface);border:1px solid var(--border);border-top:3px solid var(--brand);padding:20px;display:flex;flex-direction:column;gap:10px;position:relative}
.plan-card.popular{border-color:var(--brand);box-shadow:0 10px 26px rgba(8,104,242,0.12)}
.plan-card .pop{position:absolute;top:-11px;left:16px;background:var(--bg-dark);color:var(--mint);font-family:var(--mono);font-size:11px;letter-spacing:0.12em;padding:3px 9px}
.plan-card h3{font-size:17.5px}
/* The struck list price and the discount badge beside it, which the landing
   draws with the same two values. */
.plan-card .was{font-family:var(--mono);font-size:15px;color:var(--muted);text-decoration:line-through}
.plan-card .off{background:var(--coral);color:#fff;font-size:12.5px;letter-spacing:0.12em;padding:3px 8px}
.plan-card .price{font-size:30px;font-weight:700;color:var(--ink);font-family:var(--mono)}
.plan-card .price small{font-size:13px;font-weight:400;color:var(--muted);font-family:var(--sans)}
.plan-card .rows{display:flex;flex-direction:column;border-top:1px solid var(--border)}
.plan-card .rows>div{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid var(--border);font-size:14px}

/* ---- model panels -------------------------------------------------------- */

.model-panels{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px}
.model-panel{background:var(--surface);border:1px solid var(--border);padding:22px;display:flex;flex-direction:column;gap:12px}
.model-panel .head{display:flex;justify-content:space-between;align-items:center;gap:10px}
.model-panel .alias{font-family:var(--mono);font-size:26px;font-weight:700;color:var(--ink)}
.model-panel .tagline{color:var(--muted);font-size:14.5px;margin:0}
.model-panel ul{margin:0;padding-left:18px;font-size:14.5px;display:flex;flex-direction:column;gap:6px}
.model-panel .w{display:flex;flex-direction:column;border-top:1px solid var(--border)}
.model-panel .w>div{display:flex;justify-content:space-between;gap:10px;padding:7px 0;border-bottom:1px solid var(--border);font-size:13.5px}
.model-panel .w b{font-family:var(--mono)}

/* ---- auth ---------------------------------------------------------------- */

.auth-wrap{min-height:100vh;display:grid;place-items:center;padding:24px;background:radial-gradient(ellipse 70% 40% at 50% -10%, rgba(8,104,242,0.12), transparent 60%),var(--bg)}
.auth-card{width:min(440px,100%);background:var(--surface);border:1px solid var(--border);border-top:3px solid var(--brand);padding:32px}
.auth-card .brandline{display:flex;justify-content:center;margin-bottom:18px}
.auth-card img{height:46px}
.auth-card h1{font-size:21px;margin:0 0 8px;text-align:center}
.auth-card .lead{text-align:center;color:var(--muted);font-size:14.5px;margin:0 0 18px}
/* The design wrote this pair of buttons as an inline flex row. */
.auth-actions{display:flex;gap:10px;flex-wrap:wrap}
.auth-actions .btn{flex:1}
/* A button that submits something is wrapped in a form, and the form is the
   flex item: without this it sizes to its content while the button beside it
   grows, which is the rotate button on the key-unavailable page. */
.auth-actions form{flex:1;display:flex}

/* The same card, anchored near the top of the viewport instead of centred in
   it. Two pages are drawn on it, and both are read rather than filled in: the
   key that is shown once, and the refusal a second claim gets.
   The wrapper above stretches its single grid row to the full height and
   centres the card in it, so where the card sits depends on how tall the
   viewport is — which on a phone changes by the height of the URL bar the
   moment the page scrolls, moving the key out from under the thumb reaching
   for it. Starting the row's content pins it to the top instead, and small
   viewport units are the height measured with that bar showing, which is the
   one that does not change when it hides. */
.auth-wrap--top{align-content:start;min-height:100svh;padding-top:clamp(48px,12vh,120px)}
.kv{display:grid;grid-template-columns:auto 1fr;gap:8px 18px;font-size:14.5px;margin:10px 0 0}
.kv dt{color:var(--muted)}
.kv dd{margin:0;font-weight:600}

/* ---- footer -------------------------------------------------------------- */

.page-foot{border-top:1px solid var(--border);padding-top:16px;font-size:13px;color:var(--muted);display:flex;flex-direction:column;gap:4px}

/* ---- responsive ---------------------------------------------------------- */

@media (max-width:980px){
  .app{grid-template-columns:1fr}
  .sidebar{position:fixed;inset:0 24% 0 0;z-index:120;transform:translateX(-102%);transition:transform 200ms ease;overflow-y:auto}
  .sidebar.open{transform:none;box-shadow:0 0 0 100vmax rgba(7,27,74,0.45)}
  .menu-btn{display:inline-flex}
  .credit-hero{grid-template-columns:1fr}
  .credit-hero .side{border-left:0;padding-left:0;border-top:1px solid var(--border);padding-top:14px}
  .topbar .plan-chip .plan-name{display:none}
}
@media (max-width:760px){
  table.stack thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  table.stack tr{display:block;border-bottom:6px solid var(--bg);padding:6px 0}
  table.stack td{display:grid;grid-template-columns:44% 1fr;gap:10px;border-bottom:1px dotted var(--border)}
  table.stack td::before{content:attr(data-label);font-size:11px;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);padding-top:2px}
  td.r{text-align:left}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important}
  /* An infinite spinner shortened to 0.01ms is a strobe, not a reduction. */
  .btn[aria-busy="true"]::after,.portal-busy::after{animation:none}
}

/* ---- dark theme ---------------------------------------------------------- */

/* Carried over from the prototype with the tokens renamed to this file's. One
   class on <html> is the whole mechanism: theme.js puts it there before the
   first paint and the button in the topbar flips it, and everything below is
   what answers. */
html.dark{color-scheme:dark}
html.dark body{
  --bg:#060F26; --bg-alt:#0B1B3F; --surface:#0A1730; --ink:#E6EEFC; --muted:#A6B8DA; --border:#1B2C55;
  --ok:#7FDCC8; --ok-bg:rgba(141,223,208,0.12); --warn:#FFB38A; --warn-bg:rgba(255,154,107,0.12);
  --err:#FF9C94; --err-bg:rgba(179,38,30,0.18); --info:#7FBDFF; --info-bg:rgba(8,104,242,0.18);
  --brand-ink:#5EA4FF;
}
html.dark .brand{background:#0E1F42;border-bottom-color:var(--border)}
html.dark .brand img{filter:brightness(1.35) saturate(1.1)}
html.dark .brand .env{background:rgba(8,104,242,0.22);border-color:rgba(94,164,255,0.5);color:#9CC6FF}
html.dark .lang-switch a,html.dark .switcher button,html.dark .switcher a,html.dark .user-menu>summary,html.dark .user-menu .menu a,html.dark .user-menu .menu button,html.dark .btn.quiet{background:var(--surface);color:var(--muted)}
html.dark .user-menu .menu a:hover,html.dark .user-menu .menu button:hover{background:var(--bg-alt);color:var(--ink)}
html.dark .btn.quiet:hover{color:var(--ink)}
html.dark .field input,html.dark .field select,html.dark .field textarea{background:var(--surface);color:var(--ink)}
html.dark tbody tr:hover{background:#0D1E3E}
html.dark .badge.err{color:#FFB4AD}
html.dark .badge.solid-ok{background:#12B981;color:#FFFFFF;border-color:#12B981;font-weight:700}
html.dark .badge.neutral{color:#B6C6E4;background:rgba(93,109,139,0.22);border-color:rgba(150,170,205,0.45)}
html.dark .skl{background:linear-gradient(90deg,var(--bg-alt),#12244A,var(--bg-alt));background-size:200% 100%}
html.dark .auth-wrap{background:radial-gradient(ellipse 70% 40% at 50% -10%, rgba(8,104,242,0.25), transparent 60%),var(--bg)}
html.dark .plan-card.popular{box-shadow:0 10px 26px rgba(8,104,242,0.3)}
/* The panel itself, which the design never flipped (styles.css:84): its rows
   answer the rule above and its own background stayed #fff, so the customer
   menu opened as a white card with dark rows inside it. The .who line keeps
   var(--muted), which flips with the theme on its own. */
html.dark .user-menu .menu{background:var(--surface);border-color:var(--border);color:var(--ink);box-shadow:0 12px 28px rgba(0,0,0,0.5)}
html.dark pre.code{background:#050B1E}
html.dark .chart .tick{fill:var(--muted)}
html.dark .chart-tip{background:#02060F;border-color:var(--sky)}
html.dark .credit-hero .big,
html.dark .card .big,
html.dark .plan-card .price,
html.dark .model-panel .alias,
html.dark .tabs button[aria-selected="true"],
html.dark .menu-btn{color:#EAF2FF}
html.dark .user-menu .avatar{background:var(--brand)}
html.dark a{color:#7FBDFF}
html.dark a:hover{color:#B8DBFF}

/* The toggle itself. Both icons are in the button and one of them is on
   screen: the moon while the page is light, the sun once it is dark. */
.theme-toggle{min-width:44px;min-height:44px;background:#fff;border:1px solid var(--border);color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center}
.theme-toggle:hover{color:var(--ink);border-color:var(--muted)}
.theme-toggle .sun{display:none}
html.dark .theme-toggle{background:var(--surface)}
html.dark .theme-toggle .sun{display:block}
html.dark .theme-toggle .moon{display:none}
