/* Shared shell for every page. One stylesheet rather than a copy per page:
   the pages differ in words, not in design, and divergent copies of the same
   tokens is how a brand stops looking like one. The marketing pages layer
   their own <style> on top of this; the token block below is the only copy. */

:root {
  --bg:#0B0D12; --ink:#F2F4F8; --mut:#9BA3B5; --card:rgba(255,255,255,.045);
  --edge:rgba(255,255,255,.09); --vio:#7C6CFF; --cyan:#4AD9E8; --mag:#E86CD4;
  --pass:#34D399; --block:#F87171; --amber:#FBBF24;
  /* Layout width. See .wrap. Pages override --wrap-base, never --wrap. */
  --wrap-base:960px; --wrap:var(--wrap-base);
  /* Body text, same shape: a page picks the base, the breakpoints scale it.
     The three marketing pages redefine `body` with the `font` SHORTHAND,
     which resets font-size, and a page style loads after this file — so a
     plain `@media { body { font-size } }` here was silently overridden on
     exactly those pages. Routing it through a variable is what makes one
     definition actually reach all of them. */
  --body-base:16.5px; --body-size:var(--body-base); --body-line:1.72;
}

/* EVERY var() below carries a fallback equal to the value it replaced, and
   that is not defensive habit — it is required.
   .
   HTML and CSS are separate files with separate cache lifetimes, so a
   returning visitor can load NEW markup against an OLD stylesheet on the
   first request after a deploy. Without a fallback, `max-width:var(--wrap)`
   is invalid at computed-value time, which resolves to `unset` — the column
   loses its width entirely and the page renders full-bleed. Measured on the
   owner's own browser minutes after this shipped: a 3440px viewport rendered
   a 3440px column at the browser's default 16px, because the cached
   stylesheet had no --wrap in it.
   With the fallback the same stale combination renders the PREVIOUS design,
   which is the correct way for this to degrade. */
* { box-sizing:border-box; margin:0; }
/* PHONES: nothing may widen the page. `overflow-x:clip` alone hid the aurora
   blobs' overflow only where `clip` is supported (Safari 16+). Older iOS
   Safari and several in-app browsers drop the declaration, lay the page out
   543px wide and let it pan sideways — the "mobile view is not good" report
   of 2026-09-05. `hidden` is stated first as the fallback; a browser that
   understands `clip` takes the later value. The blobs are ALSO clipped at
   their own container below, so they never contribute scrollable overflow in
   the first place (a filter's halo is ink overflow, not scroll). */
html { overflow-x:hidden; overflow-x:clip; }
body { background:var(--bg); color:var(--ink); overflow-x:hidden; overflow-x:clip;
  font:var(--body-size, 16.5px)/var(--body-line, 1.72) "Inter", system-ui, sans-serif; }
.mono, code { font-family:"JetBrains Mono", ui-monospace, monospace;
  font-size:.9em; }
code { background:rgba(255,255,255,.06); border-radius:5px; padding:1px 5px;
  overflow-wrap:break-word; }
/* A command longer than the screen scrolls inside its own box; before this it
   ran under the page's clip and the reader saw half of it (C9's
   `gh release view …`, 647px on a 390px phone). */
pre { max-width:100%; overflow-x:auto; -webkit-overflow-scrolling:touch; }

/* BASE LINK COLOUR. There was none until 2026-08-30, so any link outside the
   handful of scoped rules below (nav, footer, .prose, .sheet, .steps) fell
   through to the user agent's own palette — #0000EE unvisited and #551A8B
   once VISITED. Against --bg those measure 2.07:1 and 1.76:1; WCAG 1.4.3
   wants 4.5:1, and the visited state is the one people actually saw, which
   is why the report was "purple text I cannot read". It hit exactly the
   links a paying customer reaches last: the refund policy on /account and
   the scan packs on /pricing.
   --cyan is 11.46:1 here, and is already what the scoped rules use, so this
   makes the default agree with them rather than introducing a fourth colour.
   :visited is stated explicitly: without it the UA default wins again the
   moment a page is revisited, which is exactly how this hid for so long. */
a { color:var(--cyan); }
a:visited { color:var(--cyan); }

/* Wider than a marketing column: policy and terms are read in long passes,
   and a 760px measure made them feel like fine print.
   .

   --wrap-base is the width a PAGE picks (the marketing pages want 1080 for
   four tier cards; prose and the account want 960). --wrap is what the
   breakpoints below compute FROM it, so a page states its base once and never
   restates the scaling. CSS variables are late-bound, so a page overriding
   --wrap-base changes every calc below it even though those live in this
   file and load first. That is the whole point: the three marketing pages
   each carried their own copy of `.wrap { max-width:1080px }`, and a copy of
   a rule is how the responsive nav fix reached every page except the three
   people actually arrive on. */
.wrap { max-width:var(--wrap, 960px); margin:0 auto; padding:0 32px 90px;
  position:relative; }

/* LARGE DISPLAYS. On a 2560- or 3440-wide screen a 960px column is a ribbon
   down the middle with most of the display unused, and 16.5px text on a dense
   panel is genuinely small. Both grow with the viewport.

   Every rule here is min-width ONLY. Nothing below 1440px is touched, so
   phones and ordinary laptops render exactly as before — the base values are
   the same bytes they always were, and these queries simply never match.

   The column grows more slowly than the viewport on purpose: line length is
   still a reading constraint, and .prose keeps its own 78ch cap regardless,
   so long-form pages never widen past a comfortable measure however big the
   screen is. What gains the width is what needs it — tier cards, the account
   grid, tables. */
@media (min-width:1440px) { :root { --wrap:calc(var(--wrap-base) + 200px); } }
@media (min-width:1760px) { :root { --wrap:calc(var(--wrap-base) + 380px); } }
@media (min-width:2200px) { :root { --wrap:calc(var(--wrap-base) + 560px); } }
/* 3440x1440 ultrawide. At the 2200 step that display still showed a 1640px
   column inside 3440px of screen; this brings it to 1880. Deliberately not
   proportional to the viewport: past roughly this width the limit stops being
   the screen and starts being the eye, and a 2600px row of tier cards is
   harder to read, not easier. */
@media (min-width:3000px) { :root { --wrap:calc(var(--wrap-base) + 800px); } }

/* Text size follows the display, not the column. A high-DPI 27" panel renders
   16.5px physically smaller than a 13" laptop does; rem-based sizing then
   carries it through the whole page. */
@media (min-width:1760px) { :root { --body-size:calc(var(--body-base) + 1px); } }
@media (min-width:2200px) { :root { --body-size:calc(var(--body-base) + 2px); } }
@media (min-width:3000px) { :root { --body-size:calc(var(--body-base) + 2.5px); } }

.aurora { position:absolute; inset:-200px 0 auto 0; height:440px;
  pointer-events:none; z-index:0; filter:blur(90px); opacity:.28;
  overflow:hidden; }
.aurora i { position:absolute; border-radius:50%; }
.aurora i:nth-child(1){ width:420px;height:420px;background:var(--vio); left:2%; top:60px; }
.aurora i:nth-child(2){ width:320px;height:320px;background:var(--cyan); right:6%; top:10px; opacity:.7; }

nav { display:flex; align-items:center; gap:12px; padding:26px 0;
  position:relative; z-index:2; }
nav .home { display:flex; align-items:center; gap:10px; text-decoration:none;
  color:var(--ink); }
nav .home b { font-weight:800; font-size:18px; letter-spacing:-.2px; }
nav .home:hover b { opacity:.85; }
nav .links { margin-left:auto; display:flex; gap:24px; font-size:14.5px;
  align-items:center; flex-wrap:wrap; }
nav a { color:var(--mut); text-decoration:none; }
nav a:hover, nav a[aria-current] { color:var(--ink); }
nav .pill { background:linear-gradient(90deg,var(--vio),var(--cyan));
  color:#fff; padding:9px 18px; border-radius:999px; font-weight:600; }

header.page { position:relative; z-index:1; padding:40px 0 8px; }
header.page h1 { font-size:clamp(30px,4.6vw,44px); font-weight:800;
  letter-spacing:-1.5px; line-height:1.08; }
header.page .sub { color:var(--mut); margin-top:12px; font-size:17px; }

.prose { position:relative; z-index:1; max-width:78ch; padding-top:26px; }
.prose h2 { font-size:22px; font-weight:700; letter-spacing:-.5px;
  margin:38px 0 12px; }
.prose h3 { font-size:17px; font-weight:700; margin:26px 0 8px; }
.prose p, .prose li { color:var(--mut); }
.prose p + p { margin-top:14px; }
.prose ul, .prose ol { margin:12px 0 0 22px; }
.prose li { margin:7px 0; }
.prose b, .prose strong { color:var(--ink); font-weight:600; }
.prose a { color:var(--cyan); }
.prose hr { border:none; border-top:1px solid var(--edge); margin:34px 0; }

.callout { background:var(--card); border:1px solid var(--edge);
  border-radius:14px; padding:20px 24px; margin:26px 0; }
.callout.key { border-left:3px solid var(--vio); border-radius:0 14px 14px 0; }
.callout p { color:var(--mut); }
.callout b { color:var(--ink); }

table.tbl { width:100%; border-collapse:collapse; font-size:15px;
  margin:18px 0; }
table.tbl th, table.tbl td { text-align:left; padding:11px 14px;
  border-bottom:1px solid var(--edge); vertical-align:top; }
table.tbl th { color:var(--mut); font-size:12px; text-transform:uppercase;
  letter-spacing:.06em; font-weight:600; }
table.tbl td { color:var(--mut); }
table.tbl td b { color:var(--ink); }
table.tbl td, table.tbl th { overflow-wrap:break-word; }

/* Tables on a phone. Every table on the site sits in a .tablewrap (the docs
   and pricing pages each declare the wrapper's overflow). Three columns of
   prose at 390px collapse to one word per line, so below 640px the table
   keeps a readable measure and the WRAPPER scrolls; the row label is pinned
   so the reader knows which row they are in, and the right edge fades so the
   scroll is visible rather than discovered. */
.tablewrap { position:relative; overflow-x:auto; -webkit-overflow-scrolling:touch; }
@media (max-width:640px) {
  .tablewrap table { min-width:600px; }
  .tablewrap th:first-child, .tablewrap td:first-child {
    position:sticky; left:0; z-index:1; background:var(--bg, #0B0D12); }
  .tablewrap { -webkit-mask-image:linear-gradient(90deg, #000 calc(100% - 28px), transparent);
    mask-image:linear-gradient(90deg, #000 calc(100% - 28px), transparent); }

  /* .tablewrap.stack opts OUT of the scroll-with-affordance treatment above.
     That pattern is right for short data cells — the roadmap's are full
     paragraphs, and forcing a 600px table into a 375px screen means
     swipe-scrolling sideways mid-sentence to keep reading. One card per row
     instead: the first cell (already the row's <b>-led subject) becomes the
     card's heading with no label of its own, and every other cell prints
     its own <th> text above it via data-label — the header row is still
     there for a screen reader, just visually hidden, so nothing observed
     only through the mask is lost for anyone not seeing it. */
  .tablewrap.stack { overflow-x:visible; -webkit-mask-image:none; mask-image:none; }
  .tablewrap.stack table { min-width:0; }
  .tablewrap.stack thead { position:absolute; width:1px; height:1px;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }
  .tablewrap.stack tbody, .tablewrap.stack tr { display:block; }
  .tablewrap.stack tr { background:var(--card); border:1px solid var(--edge);
    border-radius:14px; padding:16px 18px; margin:0 0 12px; }
  .tablewrap.stack tr:last-child { margin-bottom:0; }
  .tablewrap.stack td, .tablewrap.stack td:first-child {
    display:block; padding:0; border-bottom:none;
    position:static; background:transparent; }
  .tablewrap.stack td:not(:first-child) { margin-top:12px; }
  .tablewrap.stack td:not(:first-child)::before {
    content:attr(data-label); display:block; color:var(--mut);
    font-size:12px; text-transform:uppercase; letter-spacing:.06em;
    font-weight:600; margin-bottom:4px; }
}

/* --- documentation layout ---------------------------------------------------
   The docs pages (docs/, the two guides, the roadmap) are long: seven to
   twelve sections each. Two things make them read as one designed page
   rather than a scroll of prose: a contents column that stays put on a
   desktop (app.js fills it from the h2s, so it can never drift from the
   page), and a content column whose TEXT keeps a reading measure while its
   tables, figures and paired cards use the full width. Below 1100px the
   contents column disappears and the content takes the page. */
.docs-layout { position:relative; z-index:1; display:grid;
  grid-template-columns:minmax(0,1fr); gap:0 56px; }
.docs-main { min-width:0; max-width:880px; }
.docs-main .prose { max-width:none; }
.docs-main .prose > p, .docs-main .prose > ul, .docs-main .prose > ol,
.docs-main .prose > h3 { max-width:78ch; }
.docs-main .prose h2 { font-size:24px; margin:48px 0 14px; padding-top:30px;
  border-top:1px solid var(--edge); }
.docs-main .prose h2:first-child, .docs-main .prose > .callout + h2,
.docs-main .prose > .duo + h2 { margin-top:36px; }
.docs-main .prose > h2:first-child { padding-top:0; border-top:0; margin-top:8px; }
/* Below 1100px the contents column is a row of chips under the header — a
   phone still gets a jump list; above it, a sticky column beside the text. */
.toc { display:flex; flex-wrap:wrap; gap:8px; margin:22px 0 4px; font-size:13px;
  line-height:1.3; }
.toc .h { flex-basis:100%; color:var(--mut); font-size:11px; letter-spacing:.08em;
  text-transform:uppercase; font-weight:700; margin:0 0 2px; }
.toc a { display:inline-block; color:var(--mut); text-decoration:none;
  padding:7px 12px; border:1px solid var(--edge); border-radius:999px;
  background:var(--card); }
.toc a:hover, .toc a.on { color:var(--ink); border-color:rgba(255,255,255,.22); }
.toc .guides { flex-basis:100%; display:flex; flex-wrap:wrap; gap:8px; margin-top:6px; }
.toc .guides a { color:var(--cyan); border-color:transparent; background:none;
  padding-left:0; }
@media (min-width:1100px) {
  .docs-layout { grid-template-columns:220px minmax(0,1fr); }
  .toc { display:block; position:sticky; top:22px; align-self:start;
    margin:0; padding-top:26px; font-size:13.5px; line-height:1.45;
    max-height:calc(100vh - 44px); overflow-y:auto; }
  .toc .h { margin:0 0 10px 12px; }
  .toc a { display:block; padding:6px 12px; border:0; border-left:2px solid var(--edge);
    border-radius:0; background:none; overflow-wrap:anywhere; }
  .toc a:hover { color:var(--ink); }
  .toc a.on { color:var(--ink); border-color:var(--vio); }
  .toc .guides { display:block; margin-top:24px; }
  .toc .guides a { border-left-color:transparent; color:var(--cyan); padding-left:12px; }
  .toc .guides .h { margin-left:12px; }
}

/* Code blocks and screenshots inside the docs, once. Three pages carried the
   same eight lines each until 2026-09-10. */
.prose pre { background:#0C0F16; border:1px solid var(--edge); border-radius:10px;
  padding:12px 14px; overflow-x:auto; margin:12px 0 0; color:var(--mut);
  font:400 12.5px/1.55 "JetBrains Mono", ui-monospace, monospace; white-space:pre; }
.prose pre code { background:none; padding:0; font-size:inherit; color:inherit; }
.shot { margin:18px 0 24px; }
.shot img { display:block; width:100%; height:auto; border:1px solid rgba(255,255,255,.12);
  border-radius:10px; }
.shot figcaption { font-size:.93rem; line-height:1.5; color:var(--mut); margin-top:9px; }

/* Paired cards: what it does beside what it does not, the site's twin rule
   as a layout rather than two dense paragraphs. */
.duo { display:grid; grid-template-columns:1fr; gap:16px; margin:26px 0; }
@media (min-width:760px) { .duo { grid-template-columns:1fr 1fr; } }
.duo .callout { margin:0; padding:20px 22px; }
.duo .callout h3 { font-size:12px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--ink); margin:0 0 12px; font-weight:700; }
.duo .callout.does h3 { color:var(--pass); }
.duo .callout.not h3 { color:var(--amber); }
.duo .callout.not { border-left-color:var(--amber); }
.duo .callout ul { margin:0 0 0 18px; }
.duo .callout li { margin:8px 0; color:var(--mut); font-size:15.5px; }
.duo .callout li b { color:var(--ink); font-weight:600; }
.duo .callout p.fine { margin-top:12px; font-size:13.5px; color:var(--mut); }

footer { position:relative; z-index:1; margin-top:70px; padding:30px 0 60px;
  border-top:1px solid var(--edge); color:var(--mut); font-size:13.5px; }
footer .flinks, footer .links { display:flex; gap:20px; flex-wrap:wrap;
  justify-content:center; margin-bottom:10px; }
footer .flinks a[aria-current="page"], footer .links a[aria-current="page"] { color:var(--ink); }
footer a { color:var(--mut); }
footer a:hover { color:var(--ink); }
/* Thumb-sized: the links measured 22px tall on a phone. */
footer .flinks a, footer .links a { display:inline-block; padding:8px 2px; }

a:focus-visible, button:focus-visible { outline:3px solid var(--cyan);
  outline-offset:2px; }
@media (max-width:640px) { .wrap { padding:0 22px 70px; } }

/* --- the nav below 860px ---------------------------------------------------
   Six links plus a call to action stop fitting at 860px. The first attempt
   let them wrap onto two centred rows, which fits but reads as a list of
   links someone forgot to style — so this is a real disclosure menu.

   Progressive enhancement, in this order:
     * with no JavaScript the links are simply visible, stacked. Nothing is
       hidden behind a control that cannot open;
     * app.js adds `data-nav="ready"` to <nav>, which is what actually hides
       the panel and shows the toggle. A broken script therefore degrades to
       the readable state rather than to an unopenable menu.  */
.navToggle { display:none; }

@media (max-width:860px) {
  nav { flex-wrap:wrap; gap:0 12px; padding:16px 0; }
  nav .home { flex:1; min-width:0; }

  .navToggle {
    display:inline-flex; align-items:center; justify-content:center;
    /* 44px: the smallest target that is reliably hittable with a thumb. */
    width:44px; height:44px; margin-right:-10px;
    background:none; border:0; border-radius:12px; cursor:pointer;
    color:var(--ink); -webkit-tap-highlight-color:transparent; }
  .navToggle:focus-visible { outline:2px solid var(--vio); outline-offset:2px; }
  .navToggle span, .navToggle span::before, .navToggle span::after {
    content:""; display:block; width:20px; height:2px; border-radius:2px;
    background:currentColor; transition:transform .22s ease, opacity .18s ease; }
  .navToggle span { position:relative; }
  .navToggle span::before { position:absolute; top:-6px; }
  .navToggle span::after  { position:absolute; top:6px; }
  /* The bars become the cross. One element, no icon swap, no layout shift. */
  nav[data-open="true"] .navToggle span { background:transparent; }
  nav[data-open="true"] .navToggle span::before { transform:translateY(6px) rotate(45deg); }
  nav[data-open="true"] .navToggle span::after  { transform:translateY(-6px) rotate(-45deg); }

  /* The panel. `visibility` rather than `display:none` so the transition can
     run and so focus cannot land inside a closed menu. */
  nav[data-nav="ready"] .links {
    position:absolute; top:100%; right:0; left:0; z-index:40;
    margin:8px 0 0; padding:10px 8px 14px;
    flex-direction:column; align-items:stretch; gap:2px;
    background:#0E1119; border:1px solid var(--edge); border-radius:16px;
    box-shadow:0 24px 60px rgba(0,0,0,.55);
    opacity:0; visibility:hidden; transform:translateY(-6px);
    transition:opacity .18s ease, transform .18s ease, visibility 0s linear .18s; }
  nav[data-nav="ready"][data-open="true"] .links {
    opacity:1; visibility:visible; transform:none;
    transition:opacity .18s ease, transform .18s ease, visibility 0s; }

  nav .links a { padding:13px 14px; border-radius:10px; font-size:16px;
    color:var(--ink); }
  nav .links a[aria-current] { background:rgba(124,108,255,.14); color:#fff; }
  nav .links a:active { background:rgba(255,255,255,.06); }
  nav .links .pill { margin-top:8px; text-align:center; padding:14px 18px;
    font-size:15.5px; }
  nav .nojs { padding:8px 14px; }
}

/* --- the Docs menu ---------------------------------------------------------
   app.js wraps the nav's Docs link in .navDocs and adds a caret button and a
   panel of the documentation grouped by category (DOCS_MENU, one copy). The
   link itself keeps working without the script; with it, hovering or the
   caret opens the panel on a desktop, and inside the phone panel the caret
   unfolds the same groups in place. */
nav .navDocs { position:relative; display:inline-flex; align-items:center; gap:2px; }
nav .navMore { background:none; border:0; color:var(--mut); cursor:pointer;
  font-size:10px; line-height:1; padding:8px 6px; border-radius:6px;
  -webkit-tap-highlight-color:transparent; }
nav .navMore:hover, nav .navDocs:hover .navMore,
nav .navDocs[data-open="true"] .navMore { color:var(--ink); }
nav .navMenu { position:absolute; top:calc(100% + 12px); left:50%;
  transform:translate(-50%, -6px); z-index:50; width:min(760px, calc(100vw - 48px));
  display:grid; grid-template-columns:repeat(auto-fit, minmax(126px, 1fr));
  gap:18px 22px; padding:20px 22px; background:#0E1119; border:1px solid var(--edge);
  border-radius:16px; box-shadow:0 24px 60px rgba(0,0,0,.55); text-align:left;
  opacity:0; visibility:hidden;
  transition:opacity .16s ease, transform .16s ease, visibility 0s linear .16s; }
nav .navDocs:hover .navMenu, nav .navDocs:focus-within .navMenu,
nav .navDocs[data-open="true"] .navMenu { opacity:1; visibility:visible;
  transform:translate(-50%, 0); transition:opacity .16s ease, transform .16s ease, visibility 0s; }
nav .navMenu h4 { font-size:11px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--mut); margin:0 0 8px; font-weight:700; }
nav .navMenu a { display:block; color:var(--ink); font-size:14px; padding:5px 0;
  line-height:1.35; }
nav .navMenu a:hover { color:var(--cyan); }
nav .navMenu a.lead { font-weight:600; }
@media (max-width:860px) {
  nav .navDocs { display:flex; flex-wrap:wrap; align-items:stretch; }
  nav .navDocs > a { flex:1; }
  nav .navMore { width:44px; font-size:12px; }
  nav .navMenu { position:static; transform:none; width:auto; flex-basis:100%;
    grid-template-columns:1fr; gap:10px; padding:4px 0 10px 14px; background:none;
    border:0; box-shadow:none; opacity:1; visibility:visible; transition:none;
    display:none; }
  nav .navDocs[data-open="true"] .navMenu { display:grid; }
  nav .navDocs:hover:not([data-open="true"]) .navMenu,
  nav .navDocs:focus-within:not([data-open="true"]) .navMenu { display:none; }
  nav .navMenu a { padding:6px 0; font-size:15px; }
}

/* Wider than the panel but narrower than comfortable: the row still fits, but
   only with a tighter gap. Measured — at 768px the default 24px tips it. */
@media (min-width:861px) and (max-width:1000px) {
  nav .links { gap:18px; font-size:14px; }
}

/* --- shared utilities ---------------------------------------------------- */
.sr-only { position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap; }
nav .nojs { color:var(--mut); font-size:12.5px; }

/* --- account components (used by /account.html) -------------------------- */
.page-body { position:relative; z-index:1; padding-top:26px; }
.acct-lede { color:var(--mut); font-size:15px; margin-bottom:16px; }
.acct { background:var(--card); border:1px solid var(--edge);
  border-radius:16px; padding:26px 28px; max-width:720px; margin:0 auto; }
/* The 720px cap above is right for the SIGNED-OUT card, which is one narrow
   panel. It was wrong for the signed-in page from the moment the two-column
   shell was added and the cap was not lifted with it: 720 - 56 padding - 28
   gap - 380 aside left the main column 254px, measured on a 3440px display
   where .wrap was offering 1760. Every panel in it — allowed repositories,
   deep scans, devices — has been wrapping in a quarter of the width the page
   already had, at every screen size, since the shell shipped. */
.acct.acct-wide { max-width:none; }
.acct h3 { font-size:17px; font-weight:700; margin-bottom:12px; }
.acct .btns { display:flex; gap:12px; flex-wrap:wrap; }
/* Anchors in a button row are BUTTONS. Four of them exist on the account page
   — Update payment method, Cancel subscription, Add to Chrome, Open console —
   and every one is an <a> because it navigates somewhere: Paddle's hosted
   pages, the Web Store, the console. Only `.acct button` was styled, so those
   four rendered as bare underlined links sitting beside real buttons in the
   same row, with the primary "Add to Chrome" call to action among them.
   Selecting both keeps one definition rather than a copy that can drift. */
.acct button, .acct .btns a { font:600 15px "Inter", system-ui, sans-serif;
  padding:12px 18px; border-radius:11px; border:1px solid var(--edge);
  background:rgba(255,255,255,.05); color:var(--ink); cursor:pointer;
  display:inline-block; text-decoration:none; }
/* :visited too — otherwise the base a:visited rule repaints a followed
   button's label cyan, which is the same class of bug as the one that made
   these links unreadable in the first place. */
.acct .btns a:visited { color:var(--ink); }
.acct button:hover, .acct .btns a:hover { background:rgba(255,255,255,.09); }
.acct button.primary, .acct .btns a.primary,
.acct .btns a.primary:visited { border-color:transparent; color:#fff;
  background:linear-gradient(90deg,var(--vio),var(--cyan)); }
.acct button.small, .acct .btns a.small { font-size:13px; padding:7px 13px; }
.acct button:disabled { opacity:.45; cursor:default; }
.kv { display:grid; grid-template-columns:auto 1fr; gap:6px 18px;
  font-size:15px; margin-bottom:16px; }
.kv dt { color:var(--mut); }
.kv dd { margin:0; }
.pill2 { font:700 11px "Inter", system-ui, sans-serif; letter-spacing:.06em;
  padding:3px 9px; border-radius:6px; }
.pill2.ok { color:var(--pass); background:rgba(52,211,153,.15); }
.pill2.warn { color:var(--amber); background:rgba(251,191,36,.16); }
.dev { display:flex; align-items:center; gap:12px; padding:9px 0;
  border-bottom:1px solid var(--edge); font-size:14px; flex-wrap:wrap; }
.dev:last-child { border-bottom:none; }
.dev .id { color:var(--mut); }
.dev button { margin-left:auto; }
/* Two actions on one row (Make owner · Remove) sit together at the end,
   not one at each side. */
.dev button + button { margin-left:0; }
/* The name is the one item that can be long (a repository slug); let it
   wrap inside its own span instead of widening the row. */
.dev > span:first-child { min-width:0; overflow-wrap:anywhere; }
/* --- Bring-your-own keys -------------------------------------------------
   A key is not a device. `.dev` puts name, id and button on one flex line,
   which is right for a device and wrong for this: a provider card carries a
   name, a consent sentence, a long secret and an action. Sharing `.dev` is
   what squeezed the field until its placeholder truncated, gave three
   providers three different input widths, and wrapped "Anthropic (Claude)"
   onto two lines.

   The field also never picked up the site's input styling, because the rule
   below is `.acct input[type=text]` and a secret is `type=password`. It was
   rendering as a raw browser control sized to its placeholder. */
.keygrid { display:grid; gap:12px; margin-top:14px;
  grid-template-columns:repeat(auto-fit, minmax(min(100%, 340px), 1fr)); }
.keycard { background:var(--card, rgba(255,255,255,.045));
  border:1px solid var(--edge, rgba(255,255,255,.09));
  border-radius:14px; padding:14px 16px 15px;
  display:flex; flex-direction:column; gap:11px; }
.kchead { display:flex; align-items:baseline; justify-content:space-between;
  gap:10px; }
.kcname { font-weight:700; font-size:15px; line-height:1.25; }
.kcstate { flex:none; font:600 11px/1 "JetBrains Mono", ui-monospace, monospace;
  letter-spacing:.06em; text-transform:uppercase; padding:4px 7px;
  border-radius:999px; color:var(--mut, #9BA3B5);
  border:1px solid var(--edge, rgba(255,255,255,.09)); }
.kcstate.on { color:var(--pass, #34D399); border-color:color-mix(in srgb,
  var(--pass, #34D399) 45%, transparent); }

/* The consent line. Set apart because it IS the contract — the one sentence
   that has to be read before the control beside it is used — not because a
   flourish was wanted here. */
.kcsends { position:relative; padding-left:11px; font-size:13px;
  line-height:1.55; color:var(--mut, #9BA3B5); }
.kcsends::before { content:""; position:absolute; left:0; top:2px; bottom:2px;
  width:2px; border-radius:2px;
  background:linear-gradient(180deg, var(--vio, #7C6CFF), var(--cyan, #4AD9E8)); }
.kcsends b { display:block; font:600 11px/1 "JetBrains Mono", ui-monospace, monospace;
  letter-spacing:.14em; text-transform:uppercase;
  color:var(--cyan, #4AD9E8); margin-bottom:4px; }

/* Wraps rather than squeezes. Keyed to the field's own basis, not a viewport
   breakpoint, because this card sits in a column whose width comes from the
   two-column shell — a media query would fire at the wrong moment. Below
   ~330px of room the button drops to its own line and the field gets the
   whole width, which is the only way the longest placeholder
   ("an Anthropic API key, starting sk-ant-") stays readable on a phone. */
.kcact { display:flex; gap:8px; align-items:center; flex-wrap:wrap;
  margin-top:auto; }
/* min-width:0 is what actually stops the overflow: a flex item defaults to
   min-width:auto, so the field refused to shrink below its placeholder and
   pushed the button out instead of wrapping. */
.kcinput { flex:1 1 240px; min-width:0; background:#0C0F16;
  border:1px solid var(--edge, rgba(255,255,255,.09)); border-radius:10px;
  padding:9px 11px; color:var(--ink, #F2F4F8);
  font:400 13px "JetBrains Mono", ui-monospace, monospace; }
.kcinput::placeholder { color:var(--mut, #9BA3B5); opacity:.65; }
.kcinput:focus-visible { outline:2px solid var(--vio, #7C6CFF);
  outline-offset:1px; border-color:transparent; }
.kcact button { flex:none; margin-left:0; }
.kchint { flex:1 1 200px; min-width:0; font-size:12.5px;
  color:var(--mut, #9BA3B5); overflow-wrap:anywhere; }

.acct input[type=text] { flex:1; min-width:200px; background:#0C0F16;
  border:1px solid var(--edge); border-radius:10px; padding:10px 12px;
  color:var(--ink); font:400 14px "JetBrains Mono", ui-monospace, monospace; }
.acct .row { display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
.acct pre { background:#0C0F16; border:1px solid var(--edge);
  border-radius:10px; padding:12px 14px; overflow-x:auto; font-size:12.5px;
  line-height:1.5; color:var(--mut); white-space:pre-wrap;
  word-break:break-word; margin-top:12px; max-height:320px; }
.acct .msg { margin-top:12px; font-size:14px; }
.acct .msg.err { color:var(--block); }
.acct hr { border:none; border-top:1px solid var(--edge); margin:20px 0; }
.acct .fine { color:var(--mut); font-size:13px; margin-top:10px; }
/* "Changing plan" and "Refunds" are one note on two lines, not two unrelated
   remarks, so they sit closer than full paragraph spacing.
   Marked with a class rather than written `.fine + .fine`: the billing box
   also holds a HIDDEN .fine (subInvoicesError) immediately above this pair,
   and an adjacent-sibling rule still matches a display:none element — so the
   sibling form quietly pulled "Changing plan" up against the buttons. */
.acct .policy + .policy { margin-top:4px; }
.acct .note { margin-top:16px; border-left:3px solid var(--vio);
  background:rgba(124,108,255,.08); border-radius:0 10px 10px 0;
  padding:12px 16px; font-size:14px; color:var(--mut); }
.acct .note b { color:var(--ink); }
/* A button row inside a .note needs separating from the sentence above it and,
   more importantly, from ANOTHER row. The extension panel holds two sibling
   .btns divs — Re-activate and Add to Chrome — because app.js shows and hides
   them independently, so the flex `gap` inside each one never applies BETWEEN
   them and the two buttons rendered touching.
   Matched at 12px, the same as that gap, so a row that wrapped and two rows
   that are separate elements are indistinguishable — which is the point.
   margin-top rather than an adjacent-sibling rule: a display:none .btns still
   matches `+`, so the sibling form would put a phantom gap above whichever
   row is visible when the other is hidden. */
.acct .note .btns { margin-top:12px; }
.acct .note.pending { border-left-color:var(--amber);
  background:rgba(251,191,36,.08); }

/* --- verdict card (readable, not raw JSON) ------------------------------- */
.vout { margin-top:14px; background:#0C0F16; border:1px solid var(--edge);
  border-radius:14px; padding:16px 18px; }
.vhead { display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.vbadge { display:inline-block; font:800 13px/1 "JetBrains Mono",
  ui-monospace, monospace; letter-spacing:.08em; padding:8px 12px;
  border-radius:8px; }
.vbadge.PASS { color:#052; background:var(--pass); }
.vbadge.REVIEW { color:#4a3305; background:var(--amber); }
.vbadge.BLOCK { color:#fff; background:#A00A12; }
.vbadge.UNCHECKED { color:var(--amber); background:rgba(251,191,36,.14); }
.vbadge.OTHER { color:var(--mut); background:rgba(255,255,255,.08); }
.vslug { font-size:14px; color:var(--ink); }
.vmeta { color:var(--mut); font-size:12.5px; margin-top:8px; }
.vmsg { color:var(--mut); font-size:14px; margin-top:8px; }
.vmsg:first-child { margin-top:0; }
.vlink { color:var(--cyan); font-size:14px; display:inline-block;
  margin-top:10px; margin-right:14px; }
.vout button.small { margin-top:10px; }
.vfindings { list-style:none; padding:0; margin:14px 0 0; }
.vfindings li { background:var(--card); border:1px solid var(--edge);
  border-radius:12px; padding:10px 14px; margin-bottom:8px; display:flex;
  gap:12px; align-items:baseline; }
.vfindings code { color:var(--ink); font-weight:600; background:none;
  padding:0; }
.vbody { min-width:0; }
.vdetail { color:var(--mut); font-size:13px; display:block; margin-top:2px; }
details.vraw { margin-top:12px; }
details.vraw summary { color:var(--mut); font-size:13px; cursor:pointer; }
details.vraw pre { margin-top:8px; }

/* --- severity chips (mirrors extension/blocked.html) --------------------- */
.chip { font-weight:800; font-size:11px; letter-spacing:.1em;
  padding:3px 8px; border-radius:6px; flex:none; }
.chip.CRIT { background:rgba(248,113,113,.16); color:var(--block); }
.chip.HIGH { background:rgba(251,191,36,.16); color:var(--amber); }
.chip.MED  { background:rgba(74,217,232,.14); color:var(--cyan); }
.chip.INFO { background:rgba(255,255,255,.07); color:var(--mut); }

/* --- modal dialogs (markup injected by app.js) ---------------------------
   Shared by the sign-in dialog (#login) and the plan-change confirm dialog
   (#confirmDlg) — both are `dialog.modal`, so the fill-the-viewport and
   backdrop rules live once instead of being copied per dialog id. */
/* Fill the viewport and centre the sheet inside it. The default dialog box
   shrink-wraps its content and sits where the UA puts it; this pins it to
   the middle at every size, and keeps the area around the sheet as the
   backdrop hit target for click-to-close. */
dialog.modal { border:none; padding:0; background:transparent;
  position:fixed; inset:0; width:100vw; height:100dvh;
  max-width:100vw; max-height:100dvh; display:grid; place-items:center; }
dialog.modal:not([open]) { display:none; }
dialog.modal::backdrop { background:rgba(6,8,13,.78);
  backdrop-filter:blur(3px); }
.sheet { width:min(520px,94vw); background:#11141C;
  border:1px solid var(--edge); border-radius:20px; padding:40px 44px 34px;
  box-shadow:0 30px 80px rgba(0,0,0,.6); color:var(--ink);
  font:16px/1.6 "Inter", system-ui, sans-serif; }
.sheet .mark { display:flex; justify-content:center; margin-bottom:18px; }
.sheet h3 { font-size:26px; font-weight:800; letter-spacing:-.7px;
  text-align:center; margin-bottom:8px; }
.sheet .sub { color:var(--mut); font-size:15.5px; text-align:center;
  margin-bottom:28px; }
/* Mark and label are centred as one group: the icon does not sit against the
   left edge with the text stretched beside it. `span` must NOT be flex:1 —
   that is what pushed the pair apart. */
.provider { display:flex; align-items:center; justify-content:center;
  gap:14px; width:100%;
  background:#fff; color:#1f1f1f; border:1px solid #dadce0;
  border-radius:12px; padding:15px 20px;
  font:600 16px "Inter", system-ui, sans-serif;
  cursor:pointer; margin-bottom:12px; }
.provider:hover { background:#f6f7f9; }
.provider:focus-visible { outline:3px solid var(--cyan); outline-offset:2px; }
.provider svg { flex:none; }
.provider span { flex:none; text-align:center; }
.sheet .fine2 { color:var(--mut); font-size:13.5px; text-align:center;
  margin-top:20px; line-height:1.6; }
.sheet .fine2 a { color:var(--cyan); }
.sheet .msg { margin-top:12px; font-size:13.5px; text-align:center; }
.sheet .msg.err { color:var(--block); }
.sheet .msg a { color:var(--cyan); }
.sheet .close { position:absolute; }
.sheetwrap { position:relative; }
.sheet .x { all:unset; cursor:pointer; position:absolute; top:14px;
  right:16px; color:var(--mut); font-size:20px; line-height:1; }
.sheet .x:hover { color:var(--ink); }
/* Two-button confirm/cancel row (#confirmDlg) — reuses .buy/.buy.fill so no
   new button styling is needed, just the row layout. */
.sheet .actions { display:flex; gap:10px; margin-top:6px; }
.sheet .actions .buy { flex:1; margin-top:0; }

/* Install steps on /account — a numbered path, not a wall of prose. */
.steps { margin:14px 0 6px 20px; padding:0; color:var(--mut); font-size:14.5px; }
.steps li { margin:6px 0; line-height:1.55; }
.steps b { color:var(--ink); }
.steps a { color:var(--cyan); }

/* A buy control that is not for sale yet: same shape, clearly inert. */
.buy.disabled, .disabled { opacity:.45; cursor:default; pointer-events:none; }

/* ---------------------------------------------------------- account shell
   Two columns on the account page: the work on the left, who-you-are and the
   money on the right. Before 2026-08-26 everything was one centred stack, so
   identity, billing and destructive controls were interleaved with the
   operational panels and the page read as a list of unrelated boxes.

   Grid, not float, so the aside can come FIRST in the DOM on narrow screens
   (identity before tools) while sitting right on wide ones. Single column
   below 900px — the aside is not a sidebar worth 200px of a phone. */
.acct-shell { display:grid; gap:28px; align-items:start;
  /* minmax(0,1fr), not the implicit `auto`: an auto track cannot shrink
     below its content's min-content, so ONE unbreakable repository name in a
     scan or allow row (massgravel/Microsoft-Activation-Scripts) pushed the
     whole column 60px past a phone's right edge — every panel clipped. */
  grid-template-columns:minmax(0,1fr); }
.acct-main, .acct-side { min-width:0; }
@media (max-width:640px) { .acct { padding:18px 16px; } }
@media (min-width:900px) {
  .acct-shell { grid-template-columns:minmax(0,1fr) 320px; }
  .acct-side { position:sticky; top:24px; }
}
/* The aside holds a profile, a subscription box and a danger zone — dense,
   label-and-value content that wraps badly when it is narrow. The main column
   holds short paragraphs and a few inputs, and past about 1000px it is just
   long lines. So on wide screens the aside takes the extra room and the main
   column gives some back, rather than the aside staying fixed while the main
   grows to fill everything.
   At 3440 that reads 1024 / 560 instead of 1230 / 380: the subscription text
   stops wrapping every four words and its buttons sit two to a row.
   min-width only — the single-column phone layout above is untouched. */
@media (min-width:1760px) { .acct-shell { grid-template-columns:minmax(0,1fr) 440px; } }
@media (min-width:3000px) { .acct-shell { grid-template-columns:minmax(0,1fr) 560px; } }
.acct-side .panel { border:1px solid var(--edge); border-radius:12px;
  padding:18px; margin-bottom:18px; background:var(--card); }
.acct-side .panel h4 { margin:0 0 10px; font-size:13px; letter-spacing:.04em;
  text-transform:uppercase; color:var(--mut); }
.acct-side .panel .btns { flex-wrap:wrap; gap:8px; }

/* Identity block. The avatar is decoration: it must never push the email —
   the thing that actually identifies the account — out of view. */
.who-row { display:flex; align-items:center; gap:12px; margin-bottom:14px; }
.avatar { width:48px; height:48px; border-radius:50%; flex:0 0 48px;
  object-fit:cover; background:var(--edge); display:block; }
.avatar-fallback { display:flex; align-items:center; justify-content:center;
  font-weight:700; font-size:20px; color:var(--ink); }
.who-meta { min-width:0; }
/* 320px of panel and an address that can be anything: break rather than
   overflow, but shrink first so the common case does not break at all. */
.who-meta .who-email { font-weight:600; font-size:14px;
  overflow-wrap:anywhere; line-height:1.35; }
.who-meta .who-plan { font-size:13px; color:var(--mut); }

/* Erasure sits last and reads as dangerous without shouting. */
.panel-danger { border-color:color-mix(in srgb, var(--block) 45%, var(--edge)); }
.panel-danger h4 { color:var(--block); }

/* `hidden` loses to ANY author rule that sets display — the UA rule is not
   special. Both avatar elements set one, so without this the placeholder and
   the photo render side by side, which is exactly what happened on the first
   build of this panel. Specificity (0,2,0) beats the .avatar rules above. */
.avatar[hidden] { display:none; }

/* ------------------------------------------------------- support form
   The one form on the site that a visitor fills in. It composes a mailto
   rather than POSTing: the backend sends no email, and the CSP's
   `form-action` would block a mailto submission anyway (support.js). */
.sform { display:grid; gap:10px; margin:22px 0 34px;
  background:var(--card); border:1px solid var(--edge);
  border-radius:14px; padding:20px; }
.sform label { font:600 13px "Inter", system-ui, sans-serif; color:var(--ink);
  margin-top:4px; }
.sform .opt { color:var(--mut); font-weight:400; }
.sform select, .sform input, .sform textarea {
  font:inherit; font-size:15px; color:var(--ink);
  background:rgba(255,255,255,.04); border:1px solid var(--edge);
  border-radius:10px; padding:10px 12px; width:100%; }
.sform textarea { resize:vertical; min-height:120px; line-height:1.6; }
.sform select:focus-visible, .sform input:focus-visible,
.sform textarea:focus-visible, .sform button:focus-visible {
  outline:2px solid var(--cyan); outline-offset:2px; }
.sform input::placeholder, .sform textarea::placeholder { color:var(--mut); }
/* The dark surface does not follow the option list into the OS menu on every
   platform, so state the pair the menu actually uses. */
.sform select option { background:var(--bg); color:var(--ink); }
.sform button { justify-self:start; margin-top:8px; cursor:pointer;
  border:0; color:#0B0D12; font:700 14px "Inter", system-ui, sans-serif;
  background:linear-gradient(90deg,var(--vio),var(--cyan));
  border-radius:999px; padding:11px 20px; }
.sform .msg { color:var(--mut); font-size:14px; min-height:1.2em; }
.sform .msg.err { color:var(--amber); }
