/* ============================================================
   ValidThru client portal — /app/assets/portal.css
   Source of truth: /Design kit.html (tokens + components, verbatim)
   Direction: A1 Orbit —
   Blueprint-dark default, the ring as instrument, mono micro-labels.
   Pages ship with <html data-theme="dark">; portal.js applies the
   stored preference (auto / light / dark) on load.
   ============================================================ */

/* ---- Archivo, self-hosted (same files as site/assets/fonts) ---- */
@font-face{
  font-family:'Archivo'; font-style:normal; font-weight:100 900; font-display:swap;
  src:url('fonts/archivo-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Archivo'; font-style:normal; font-weight:100 900; font-display:swap;
  src:url('fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1EFF,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

/* ============================================================
   Design kit tokens — verbatim from /Design kit.html
   Light: Signal · Dark: Blueprint (one palette, flipped)
   ============================================================ */
:root{
  --vt-font:'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --vt-mono:ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  /* type scale */
  --vt-t-xs:12.5px; --vt-t-s:13.5px; --vt-t-base:15px; --vt-t-md:17px;
  --vt-t-lg:21px; --vt-t-xl:26px; --vt-t-2xl:33px; --vt-t-3xl:41px;
  --vt-lh:1.55;

  /* spacing, 4px base */
  --vt-s1:4px; --vt-s2:8px; --vt-s3:12px; --vt-s4:16px;
  --vt-s5:24px; --vt-s6:32px; --vt-s7:48px; --vt-s8:64px;

  /* radii */
  --vt-r-s:8px; --vt-r-m:11px; --vt-r-l:14px;

  /* ---- light · Signal ---- */
  color-scheme: light;
  --vt-bg:#F5F6F8; --vt-surface:#FFFFFF; --vt-ink:#232A33; --vt-soft:#646D78; --vt-line:#E2E5E9;
  --vt-primary:#232A33; --vt-on-primary:#FFFFFF; --vt-primary-hover:#39424E;
  --vt-valid:#0FA46F; --vt-valid-text:#09724D;
  --vt-soon:#E3A424; --vt-soon-text:#7D5A0C;
  --vt-expired:#C64438; --vt-expired-text:#A93A2B;
  --vt-pill-mix:22%; --vt-pill-mix-soon:32%;

  /* ---- brand · Iris ----
     One accent across the marketing site, the client portal and the admin
     console. Verified: white on --vt-brand is 6.10:1, and
     --vt-brand-subtle-text on --vt-brand-subtle is 9.32:1 — both clear AA at
     every size, which is why the accent is this violet and not the lighter
     one it is drawn from. Status colours (valid, soon, expired) are NOT brand
     and never move: they carry meaning, and meaning does not follow fashion. */
  --vt-brand:#6D3BE8; --vt-brand-hover:#5A2ED0; --vt-brand-on:#FFFFFF;
  --vt-brand-subtle:#EFEAFE; --vt-brand-subtle-text:#4C1D95;
  --vt-brand-line:color-mix(in srgb, var(--vt-brand) 38%, transparent);
  /* Nav ink, shared with the admin console (verified 5.47:1 on white). */
  --vt-nav-ink:#5C6570;
}

/* ---- dark · Blueprint ----
   Dark is NOT a device preference here. The portal has one appearance at a
   time and an owner decides it (settings.portal_theme, migration 0019), so
   the only thing that turns it on is data-theme="dark", written by
   assets/theme.js before first paint. prefers-color-scheme is deliberately
   not consulted: two customers on the same plan should not be looking at two
   different products. */
[data-theme="dark"]{
  color-scheme: dark;
  --vt-bg:#232A33; --vt-surface:#2C3540; --vt-ink:#F2F5F8; --vt-soft:#A5AEB9; --vt-line:#3A444F;
  --vt-primary:#F2F5F8; --vt-on-primary:#232A33; --vt-primary-hover:#DFE6EC;
  --vt-valid:#27C287; --vt-valid-text:#3BD49A;
  --vt-soon:#EAAE41; --vt-soon-text:#F2BC55;
  --vt-expired:#E66E5E; --vt-expired-text:#F79A8C;
  --vt-pill-mix:16%; --vt-pill-mix-soon:22%;
  --vt-brand:#A78BFA; --vt-brand-hover:#C4B5FD; --vt-brand-on:#232A33;
  --vt-brand-subtle:color-mix(in srgb, #A78BFA 16%, var(--vt-surface));
  --vt-brand-subtle-text:#C4B5FD;
  --vt-brand-line:color-mix(in srgb, #A78BFA 42%, transparent);
  --vt-nav-ink:#A5AEB9;
}

/* ============================ base ============================ */
*{box-sizing:border-box;margin:0;padding:0}
html,body{overflow-x:clip}
.vt-body{background:var(--vt-bg); color:var(--vt-ink);
  font-family:var(--vt-font); font-size:var(--vt-t-base); line-height:var(--vt-lh);
  -webkit-font-smoothing:antialiased; min-height:100vh}
.vt-link{color:var(--vt-ink); font-weight:600; text-decoration:underline; text-underline-offset:3px}
.vt-link:focus-visible{outline:2px solid var(--vt-ink); outline-offset:2px; border-radius:2px}

.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap}
.skip-link{position:absolute;left:16px;top:-48px;z-index:20;
  background:var(--vt-brand);color:var(--vt-brand-on);
  font:600 var(--vt-t-s)/1 var(--vt-font);padding:10px 14px;
  border-radius:var(--vt-r-s);text-decoration:none;transition:top .15s ease}
.skip-link:focus-visible{top:12px;outline:2px solid var(--vt-ink);outline-offset:2px}

/* =========================== buttons ========================== */
.vt-btn{display:inline-flex; align-items:center; gap:8px; cursor:pointer;
  font:600 var(--vt-t-base)/1 var(--vt-font); padding:12px 18px;
  border-radius:var(--vt-r-m); border:1.5px solid transparent;
  background:var(--vt-brand); color:var(--vt-brand-on);
  text-decoration:none;
  transition:background .15s ease}
.vt-btn:hover{background:var(--vt-brand-hover)}
.vt-btn--ghost{background:transparent; border-color:var(--vt-line); color:var(--vt-ink)}
.vt-btn--ghost:hover{background:color-mix(in srgb, var(--vt-ink) 6%, transparent)}
.vt-btn--quiet{background:transparent; color:var(--vt-ink); padding:12px 10px}
.vt-btn--quiet:hover{background:color-mix(in srgb, var(--vt-ink) 6%, transparent)}
.vt-btn:disabled{opacity:.45; cursor:not-allowed}
.vt-btn:focus-visible{outline:2px solid var(--vt-ink); outline-offset:2px}

/* ============================ forms =========================== */
.vt-field{display:flex; flex-direction:column; gap:6px; max-width:340px}
.vt-field > label{font:600 var(--vt-t-s)/1 var(--vt-font); color:var(--vt-ink)}
.vt-input{font:500 var(--vt-t-base)/1.3 var(--vt-font); color:var(--vt-ink);
  background:var(--vt-surface); border:1.5px solid var(--vt-line);
  border-radius:var(--vt-r-m); padding:11px 12px; width:100%}
.vt-input::placeholder{color:var(--vt-soft)}
.vt-input:focus-visible{outline:2px solid var(--vt-ink); outline-offset:1px; border-color:var(--vt-ink)}
.vt-hint{font:500 var(--vt-t-xs)/1.4 var(--vt-font); color:var(--vt-soft)}
.vt-field--error .vt-input{border-color:var(--vt-expired-text)}
.vt-field--error .vt-hint{color:var(--vt-expired-text)}
select.vt-input{appearance:auto}
input[type="date"].vt-input{min-height:44px}

/* ========================= status pills =======================
   Portal addition on the kit pill: a small icon rides beside the
   word, so status is never conveyed by color alone (WCAG). */
.vt-pill{display:inline-flex; align-items:center; gap:5px;
  font:700 11px/1 var(--vt-font); letter-spacing:.02em;
  padding:4px 9px; border-radius:999px}
.vt-pill--valid{background:color-mix(in srgb, var(--vt-valid) var(--vt-pill-mix), transparent); color:var(--vt-valid-text)}
.vt-pill--soon{background:color-mix(in srgb, var(--vt-soon) var(--vt-pill-mix-soon), transparent); color:var(--vt-soon-text)}
.vt-pill--expired{background:color-mix(in srgb, var(--vt-expired) var(--vt-pill-mix), transparent); color:var(--vt-expired-text)}
.vt-pill svg{width:11px;height:11px;flex:none}

/* ======================= ring component =======================
   --pct is how far through its lifetime the item is (0–100 elapsed).
   The ring wraps toward midnight; expired is simply a full ring.  */
.vt-ring{--pct:100; --size:36px; width:var(--size); height:var(--size);
  transform:rotate(-90deg); flex:none; display:block}
.vt-ring circle{fill:none; stroke-width:4.5}
.vt-ring__track{stroke:var(--vt-line)}
.vt-ring__arc{stroke-linecap:round; stroke-dasharray:var(--pct) 100}
.vt-ring__arc--valid{stroke:var(--vt-valid)}
.vt-ring__arc--soon{stroke:var(--vt-soon)}
.vt-ring__arc--expired{stroke:var(--vt-expired)}
.vt-ring__check{fill:none; stroke:var(--vt-ink); stroke-width:3.9; stroke-linecap:round; stroke-linejoin:round}

/* ====================== cards & item rows ===================== */
.vt-card{background:var(--vt-surface); border:1px solid var(--vt-line);
  border-radius:var(--vt-r-l); padding:var(--vt-s5)}
.vt-item{display:flex; align-items:center; gap:12px;
  background:var(--vt-surface); border:1px solid var(--vt-line);
  border-radius:var(--vt-r-m); padding:12px 14px}
.vt-item__body{flex:1; min-width:0}
.vt-item__name{font:700 14px/1.2 var(--vt-font); color:var(--vt-ink);
  overflow-wrap:anywhere}
.vt-item__meta{font:500 var(--vt-t-xs)/1.3 var(--vt-font); color:var(--vt-soft); margin-top:2px}
.vt-item__meta .due{font-weight:600;color:var(--vt-ink)}

/* Item rows that navigate (watchlist → item.html) */
a.vt-item{text-decoration:none;color:inherit;
  transition:border-color .15s ease, background .15s ease}
a.vt-item:hover{border-color:var(--vt-soft)}
a.vt-item:focus-visible{outline:2px solid var(--vt-ink);outline-offset:2px}

/* ============================ modal ===========================
   Native <dialog class="vt-modal"> — open with showModal(). */
dialog.vt-modal{background:var(--vt-surface);color:var(--vt-ink);
  border:1px solid var(--vt-line);border-radius:var(--vt-r-l);
  padding:var(--vt-s5);max-width:28rem;width:calc(100% - 32px);
  margin:auto}
dialog.vt-modal::backdrop{background:color-mix(in srgb, #10151C 60%, transparent)}
.vt-modal__title{font:700 var(--vt-t-lg)/1.25 var(--vt-font)}
.vt-modal__body{margin-top:10px;color:var(--vt-soft);font-size:var(--vt-t-s)}
.vt-modal__actions{display:flex;gap:10px;justify-content:flex-end;
  margin-top:20px;flex-wrap:wrap}

/* ======================== meter (kit vt-meter) =================
   Plan usage: items against the free cap, scans against the
   allowance. The written count is the accessible carrier; the fill
   colour only echoes it (warn = 1 left, full = none left). */
.vt-meter{display:flex;flex-direction:column;gap:6px;max-width:340px;
  font:500 var(--vt-t-s)/1.4 var(--vt-font);color:var(--vt-ink)}
.vt-meter__label{display:flex;align-items:baseline;justify-content:space-between;gap:16px}
.vt-meter__label .vt-count{font:600 12px/1 var(--vt-mono);color:var(--vt-soft);letter-spacing:.04em}
.vt-meter__track{height:6px;border-radius:999px;background:color-mix(in srgb, var(--vt-ink) 12%, transparent);overflow:hidden}
.vt-meter__fill{height:100%;border-radius:999px;background:var(--vt-valid);transition:width .4s ease}
.vt-meter--warn .vt-meter__fill{background:var(--vt-soon)}
.vt-meter--full .vt-meter__fill{background:var(--vt-expired)}

/* ===================== read-only chip (free cap) ===============
   An item above the free plan's 15-item limit after a downgrade:
   still visible, never deleted, editable again once there is room.
   Lock icon + the word — never colour alone. */
.pt-ro{display:inline-flex;align-items:center;gap:5px;flex:none;
  font:700 11px/1 var(--vt-font);letter-spacing:.02em;padding:4px 9px;border-radius:999px;
  border:1px solid var(--vt-line);color:var(--vt-soft);
  background:color-mix(in srgb, var(--vt-ink) 6%, transparent)}
.pt-ro svg{width:11px;height:11px;flex:none}

/* ====================== plan strip (watchlist) ================= */
.pt-plan{display:flex;align-items:center;gap:18px 28px;flex-wrap:wrap;
  margin-top:22px;padding:14px 16px;border:1px solid var(--vt-line);
  border-radius:var(--vt-r-m);background:var(--vt-surface);max-width:680px}
.pt-plan .vt-meter{flex:1 1 220px;max-width:320px}
.pt-plan__text{font:500 var(--vt-t-s)/1.5 var(--vt-font);color:var(--vt-soft);flex:1 1 220px}
.pt-plan__text a{white-space:nowrap}

/* ============================================================
   Portal shell
   ============================================================ */
.shell{max-width:1120px;margin:0 auto;padding:0 24px}

/* ---- sign-in: the one page with no rail (there is no session yet) ---- */
.auth-top{display:flex;align-items:center;padding:14px 24px;
  border-bottom:1px solid var(--vt-line);background:var(--vt-bg)}
.auth-top .brand{padding:0}

/* ---- the frame: sidebar + content, the console's shape ---- */
.pt-frame{display:grid;grid-template-columns:var(--pt-side, 240px) minmax(0, 1fr);
  min-height:100vh;transition:grid-template-columns .16s ease}
body[data-nav="icons"]{--pt-side:68px}
/* The chip follows the rail; the transition matches the column's. */
.pt-collapse{transition:left .16s ease, color .12s ease, border-color .12s ease}

/* z-index: the rail is `position: sticky`, which makes it a stacking context,
   so the collapse chip inside it cannot paint above the top bar on its own —
   the RAIL has to outrank the bar, or the chip's overhanging half disappears
   behind it. */
.pt-side{border-right:1px solid var(--vt-line);background:var(--vt-surface);
  display:flex;flex-direction:column;gap:2px;padding:16px 12px;
  position:sticky;top:0;z-index:6;height:100vh;overflow-y:auto;overflow-x:hidden}
.brand{display:flex;align-items:center;gap:9px;font:800 17px/1 var(--vt-font);
  letter-spacing:-0.02em;color:var(--vt-ink);text-decoration:none;flex:none;
  padding:6px 8px 14px;white-space:nowrap}
.brand:focus-visible{outline:2px solid var(--vt-ink);outline-offset:3px;border-radius:2px}

/* Nav groups and links — one design language with the admin console: the
   same paddings, the same 8px radius, the same 10px icon gap, the same
   uppercase group label, and the same selected treatment (a filled
   brand-tinted pill, no leading edge). Both rails are compared side by side
   in the review notes. */
.pt-navgroup{padding:12px 0 2px}
.pt-navgroup__title{font:700 10.5px/1 var(--vt-font);letter-spacing:.09em;
  text-transform:uppercase;color:var(--vt-soft);padding:0 8px 8px;white-space:nowrap}
.pt-nav{display:flex;flex-direction:column;gap:1px}
.pt-nav a{display:flex;align-items:center;gap:10px;min-height:34px;
  font:600 var(--vt-t-s)/1 var(--vt-font);color:var(--vt-nav-ink);
  text-decoration:none;padding:9px 8px;border-radius:var(--vt-r-s);white-space:nowrap;
  transition:background .12s ease, color .12s ease}
.pt-nav a svg{width:17px;height:17px;flex:none}
.pt-nav a:hover{color:var(--vt-ink);background:color-mix(in srgb, var(--vt-ink) 6%, transparent)}
/* Current page: a filled pill in the brand tint, and the heavier weight. The
   fill is a shape, so the state reads in grayscale and to a colour-blind eye
   with no colour doing the work alone. */
.pt-nav a[aria-current="page"]{color:var(--vt-brand-subtle-text);
  background:var(--vt-brand-subtle);font-weight:800}
.pt-nav a:focus-visible{outline:2px solid var(--vt-ink);outline-offset:-2px}

/* The foot carries sign-out, styled as a nav row so the rail reads as one
   column of the same thing. */
.pt-side__foot{margin-top:auto;padding-top:10px;border-top:1px solid var(--vt-line)}
.pt-signout{display:flex;align-items:center;gap:10px;width:100%;min-height:34px;cursor:pointer;
  font:600 var(--vt-t-s)/1 var(--vt-font);color:var(--vt-nav-ink);
  background:transparent;border:0;padding:9px 8px;border-radius:var(--vt-r-s);
  white-space:nowrap;text-align:left;transition:background .12s ease, color .12s ease}
.pt-signout:hover{color:var(--vt-ink);background:color-mix(in srgb, var(--vt-ink) 6%, transparent)}
.pt-signout:focus-visible{outline:2px solid var(--vt-ink);outline-offset:-2px}
.pt-signout svg{width:17px;height:17px;flex:none}

/* Collapse: a chip straddling the rail's right edge, out of the column so it
   never competes with a destination for the eye. */
.pt-collapse{position:fixed;top:22px;left:calc(var(--pt-side, 240px) - 13px);z-index:7;
  width:26px;height:26px;display:grid;place-items:center;cursor:pointer;
  background:var(--vt-surface);color:var(--vt-soft);
  border:1px solid var(--vt-line);border-radius:999px;padding:0;
  box-shadow:0 1px 3px color-mix(in srgb, var(--vt-ink) 12%, transparent);
  transition:color .12s ease, border-color .12s ease}
.pt-collapse:hover{color:var(--vt-ink);border-color:var(--vt-brand)}
.pt-collapse:focus-visible{outline:2px solid var(--vt-ink);outline-offset:2px}
.pt-collapse svg{width:14px;height:14px;transition:transform .16s ease}
body[data-nav="icons"] .pt-collapse svg{transform:rotate(180deg)}

/* Icons only: the labels go, the rail narrows, nothing reflows underneath. */
body[data-nav="icons"] .brand span,
body[data-nav="icons"] .pt-navgroup__title,
body[data-nav="icons"] .pt-nav__label{display:none}
body[data-nav="icons"] .pt-side{padding:16px 10px;align-items:center}
body[data-nav="icons"] .pt-nav a,
body[data-nav="icons"] .pt-signout{justify-content:center;padding:10px 0;width:44px}
body[data-nav="icons"] .brand{padding:6px 0 14px;justify-content:center}
/* No hairline between groups anywhere: the only rule in the rail is the one
   above sign-out. Without labels the groups read on spacing alone. */
body[data-nav="icons"] .pt-navgroup{padding:8px 0 2px;width:100%}
body[data-nav="icons"] .pt-navgroup + .pt-navgroup{margin-top:8px}

/* ---- the bar above the content: page name left, who you are right ---- */
.pt-top{display:flex;align-items:center;gap:16px;padding:14px 24px;
  border-bottom:1px solid var(--vt-line);background:var(--vt-bg);
  position:sticky;top:0;z-index:5}
.pt-top__title{font:700 var(--vt-t-md)/1.2 var(--vt-font);letter-spacing:-0.01em;
  color:var(--vt-ink);flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pt-top__actions{display:flex;align-items:center;gap:8px;flex:none}

/* The account cluster: a generic avatar, then the name in the logo's face. */
.pt-account{display:flex;align-items:center;gap:10px;flex:none;
  padding:4px 6px 4px 4px;border-radius:999px}
.pt-avatar{width:32px;height:32px;border-radius:50%;flex:none;
  display:grid;place-items:center;
  background:var(--vt-brand);color:var(--vt-brand-on);
  font:800 12.5px/1 var(--vt-font);letter-spacing:.01em}
.pt-account__name{font:800 var(--vt-t-s)/1 var(--vt-font);letter-spacing:-0.02em;
  color:var(--vt-ink);max-width:22ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pt-account__sub{font:600 11px/1 var(--vt-font);color:var(--vt-nav-ink);margin-top:4px}
.pt-account__text{min-width:0}

main{display:block;padding-bottom:var(--vt-s8)}

.page-head{padding:var(--vt-s6) 0 var(--vt-s4)}
.page-head h1{font:800 var(--vt-t-xl)/1.2 var(--vt-font);letter-spacing:-0.01em}
.page-head .sub{margin-top:8px;color:var(--vt-soft);font-size:var(--vt-t-s);max-width:56ch}

.micro{font:600 11px/1.4 var(--vt-mono);color:var(--vt-soft);
  letter-spacing:.12em;text-transform:uppercase}

/* `hidden` beats every display rule in this file.
   The UA sheet's [hidden]{display:none} is specificity (0,1,0) — the same as
   any class — and an author stylesheet wins a tie, so ANY `.thing{display:…}`
   silently defeats `el.hidden = true`. That is not a theoretical problem: the
   dashboard's confirm-your-email notice stayed on screen after the address was
   confirmed, because .db-unverified sets display:flex. Five pages had each
   pasted this rule into their own <style>; the dashboard had not. It belongs
   here, once. */
[hidden]{display:none !important}

/* =================== loading / empty / error ================== */
.state{display:flex;flex-direction:column;align-items:center;gap:14px;
  padding:var(--vt-s7) var(--vt-s5);text-align:center}
.state[hidden]{display:none}
.state .micro{color:var(--vt-soft)}
.state p{color:var(--vt-soft);font-size:var(--vt-t-s);max-width:44ch}
.state .vt-ring{transform:rotate(-90deg)}
.state--loading .vt-ring{animation:vt-pulse 1.6s ease-in-out infinite}
@keyframes vt-pulse{0%,100%{opacity:.35}50%{opacity:1}}
.state__actions{display:flex;gap:10px;flex-wrap:wrap;justify-content:center}

.form-status{font:500 var(--vt-t-s)/1.5 var(--vt-font);
  color:var(--vt-soft);min-height:1.5em}
.form-status--error{color:var(--vt-expired-text)}
.form-status--ok{color:var(--vt-valid-text)}

/* ============================================================
   Watchlist (A1 Orbit) — /app/
   ============================================================ */
.hero{position:relative;padding:var(--vt-s6) 0 var(--vt-s6)}
.hero::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:
    radial-gradient(1.5px 1.5px at 12% 18%, color-mix(in srgb, var(--vt-ink) 22%, transparent) 50%, transparent 51%),
    radial-gradient(1px 1px at 78% 9%,  color-mix(in srgb, var(--vt-ink) 18%, transparent) 50%, transparent 51%),
    radial-gradient(1px 1px at 90% 62%, color-mix(in srgb, var(--vt-ink) 16%, transparent) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 34% 84%, color-mix(in srgb, var(--vt-ink) 14%, transparent) 50%, transparent 51%),
    radial-gradient(1px 1px at 55% 30%, color-mix(in srgb, var(--vt-ink) 12%, transparent) 50%, transparent 51%),
    radial-gradient(1px 1px at 5% 60%,  color-mix(in srgb, var(--vt-ink) 14%, transparent) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 68% 88%, color-mix(in srgb, var(--vt-ink) 12%, transparent) 50%, transparent 51%),
    radial-gradient(1px 1px at 96% 34%, color-mix(in srgb, var(--vt-ink) 18%, transparent) 50%, transparent 51%)}
.hero-grid{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,6fr);
  gap:48px;align-items:center}
.hero-copy h1{font:800 clamp(28px, 3.6vw, var(--vt-t-2xl))/1.15 var(--vt-font);
  letter-spacing:-0.015em}
.hero-copy .sub{margin-top:12px;color:var(--vt-soft);max-width:44ch;
  font-size:var(--vt-t-s)}
.hero-cta{display:flex;gap:12px;margin-top:22px;flex-wrap:wrap}
.hero-counts{display:flex;gap:8px 20px;flex-wrap:wrap;margin-top:26px;
  font:500 var(--vt-t-s)/1.4 var(--vt-font);color:var(--vt-soft);
  list-style:none}
.hero-counts b{font-weight:700;color:var(--vt-ink)}
.hero-counts .c-soon{color:var(--vt-soon-text);font-weight:700}
.hero-counts .c-expired{color:var(--vt-expired-text);font-weight:700}

/* the orbit chart */
.orbit-wrap{display:flex;flex-direction:column;align-items:center;gap:16px;min-width:0}
.orbit{width:min(440px,100%);height:auto;display:block}
.orbit .track{fill:none;stroke:var(--vt-line);stroke-width:2.5}
.orbit .guide{fill:none;stroke:var(--vt-line);stroke-width:1;opacity:.45}
.orbit .tick{stroke:var(--vt-ink);stroke-width:2.5;stroke-linecap:round}
.orbit .midnight-label{font:700 11px var(--vt-mono);fill:var(--vt-soft);
  letter-spacing:.14em;text-transform:uppercase;text-anchor:middle}
.orbit .dot{stroke:var(--vt-bg);stroke-width:3}
.orbit .dot--valid{fill:var(--vt-valid)}
.orbit .dot--soon{fill:var(--vt-soon)}
.orbit .dot--expired{fill:var(--vt-expired)}
.orbit .num{font:700 11px var(--vt-mono);fill:var(--vt-soft);text-anchor:middle;dominant-baseline:middle}
.orbit .big{font:800 64px var(--vt-font);fill:var(--vt-ink);text-anchor:middle;letter-spacing:-0.02em}
.orbit .big-label{font:600 12px var(--vt-mono);fill:var(--vt-soft);text-anchor:middle;letter-spacing:.12em;text-transform:uppercase}
.orbit-hint{width:min(440px,100%);font:500 var(--vt-t-xs)/1.5 var(--vt-font);
  color:var(--vt-soft);text-align:center}

/* the list below */
.watchlist-slice{padding:var(--vt-s6) 0 0;border-top:1px solid var(--vt-line)}
.slice-head{display:flex;align-items:baseline;justify-content:space-between;
  gap:16px;flex-wrap:wrap;margin-bottom:6px}
.slice-head h2{font:800 var(--vt-t-lg)/1.2 var(--vt-font);letter-spacing:-0.01em}
.slice-head .tally{font:500 var(--vt-t-s)/1.4 var(--vt-font);color:var(--vt-soft)}
.slice-sub{color:var(--vt-soft);font-size:var(--vt-t-s);max-width:56ch;margin-bottom:22px}
.watchlist{display:flex;flex-direction:column;gap:10px;max-width:680px;list-style:none}
.watchlist .orbit-n{font:700 11px/1 var(--vt-mono);color:var(--vt-soft);
  flex:none;width:16px;text-align:right}
.watchlist-foot{margin-top:14px;font:500 var(--vt-t-xs)/1.5 var(--vt-font);color:var(--vt-soft)}

/* ============================================================
   Sign in — /app/signin.html
   ============================================================ */
/* Sign-in: one centred column, one primary action. The page is the whole
   screen rather than a panel inside the app shell — there is nothing else to
   do here, and every convention a person has met elsewhere puts it centre. */
.auth-wrap{display:grid;place-items:center;min-height:calc(100vh - 140px);
  padding:var(--vt-s6) var(--vt-s4) var(--vt-s7)}
.auth-card{width:100%;max-width:380px;display:flex;flex-direction:column;gap:20px}
.auth-head{display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center}
.auth-mark{margin-bottom:4px}
.auth-card h1{font:800 var(--vt-t-xl)/1.15 var(--vt-font);letter-spacing:-0.02em}
.auth-card .sub{color:var(--vt-soft);font-size:var(--vt-t-s)}
.auth-swap{background:none;border:0;padding:0;font:inherit;font-weight:700;cursor:pointer}
.auth-card .vt-field{max-width:none}
.auth-card form{display:flex;flex-direction:column;gap:14px}
.auth-primary{width:100%;justify-content:center}
.auth-note{font:500 var(--vt-t-xs)/1.5 var(--vt-font);color:var(--vt-soft);text-align:center;
  margin-top:-4px}
.auth-foot{font:500 var(--vt-t-xs)/1.5 var(--vt-font);color:var(--vt-soft);text-align:center}
.auth-actions{display:flex;flex-direction:column;gap:10px}
.auth-actions .vt-btn{justify-content:center}
.auth-divider{display:flex;align-items:center;gap:12px;
  font:600 11px/1 var(--vt-mono);color:var(--vt-soft);
  letter-spacing:.12em;text-transform:uppercase}
.auth-divider::before,.auth-divider::after{content:"";flex:1;height:1px;background:var(--vt-line)}
.auth-alt{display:flex;flex-direction:column;gap:12px}
/* First and last name side by side; stacked where there is no room for two. */
.auth-row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
@media (max-width:420px){ .auth-row{grid-template-columns:1fr} }
/* Each pane is its own form, so the browser offers the right autofill and the
   right submit button rather than one form pretending to be three. */
#signin-form,#code-form,#register-form{display:flex;flex-direction:column;gap:14px}

/* ============================================================
   Quick add — /app/add.html
   ============================================================ */
.add-form{display:flex;flex-direction:column;gap:20px;max-width:440px}
.add-form .vt-field{max-width:none}
.date-preview{font:500 var(--vt-t-s)/1.5 var(--vt-font);color:var(--vt-soft);
  min-height:1.5em}
.date-preview b{font-weight:700;color:var(--vt-ink)}

.extras{border:1px solid var(--vt-line);border-radius:var(--vt-r-l);
  background:var(--vt-surface)}
.extras summary{cursor:pointer;padding:14px 16px;
  font:700 var(--vt-t-s)/1.3 var(--vt-font);color:var(--vt-ink);list-style:none}
.extras summary::-webkit-details-marker{display:none}
.extras summary::before{content:"+";display:inline-block;width:18px;color:var(--vt-soft)}
.extras[open] summary::before{content:"–"}
.extras summary:focus-visible{outline:2px solid var(--vt-ink);outline-offset:-2px;border-radius:var(--vt-r-l)}
.extras .body{padding:0 16px 18px;display:flex;flex-direction:column;gap:16px}

.form-actions{display:flex;gap:12px;align-items:center;flex-wrap:wrap}

/* ========================= responsive ========================= */
@media (max-width: 920px){
  .hero-grid{grid-template-columns:1fr;gap:36px}
  .orbit-wrap{order:-1}
}
@media (max-width: 860px){
  /* Below a laptop the rail is icons whatever the stored preference says:
     there is no room for labels and a horizontal scroller is worse than an
     icon rail you can still read by its tooltips. */
  .pt-frame{grid-template-columns:64px minmax(0, 1fr)}
  .brand span, .pt-navgroup__title, .pt-nav__label{display:none}
  .pt-side{padding:16px 8px;align-items:center}
  .pt-nav a, .pt-signout{justify-content:center;padding:10px 0;width:44px}
  .brand{padding:6px 0 14px;justify-content:center}
  .pt-collapse{display:none}
}
@media (max-width: 640px){
  .shell{padding:0 16px}
  .pt-top{padding:12px 16px}
  .pt-account__name{max-width:12ch}
}

/* ====================== reduced motion ======================== */
@media (prefers-reduced-motion: reduce){
  .vt-btn, .vt-input, a.vt-item, .skip-link{transition:none}
  .state--loading .vt-ring{animation:none;opacity:.7}
}
