/* VarnaOps redesign, stage 2: the SIGNED-IN pages and the small leftover pages.
   login.html, account.html, runs.html, 404.html, and the two retired stubs
   (contact.html, changelog.html).

   WHY A SECOND FILE AND NOT MORE OF redesign.css. redesign.css is stage 1's sheet: the
   tokens, the chrome (top bar, footer, buttons) and the reading column the nine text pages
   use. It is owned by the branch that is rebuilding index.html into a landing page and
   moving the wizard to app.html, and two writers in one file is a conflict per save. So
   everything only these pages need lives here, and every page that links this file links
   redesign.css FIRST: the tokens, `.btn`, `.topbar`, `.foot`, `.ruo-callout` and the
   account-menu rules all come from there and are not repeated.

   THE CLASS NAMES ARE THE LIVE PAGES' OWN, deliberately, and this is the same decision
   stage 1 made for the text pages. login.js, account.js and runs.js emit their markup as
   strings, and those strings are what a dozen test files read: the terms gate is
   `.card-box.terms-gate` because tests/test_terms_acceptance_web.py matches that exact
   attribute, `.acct-row` because tests/test_account_menu.py's ban list is written against
   it, `.run-status--ok` because that is what runs.js computes. Renaming them to the mock's
   `.card` / `.row` / `.st-complete` would have been a rewrite of three scripts and their
   guards to change how the pages look. So the DESIGN is reached by pointing the mock's
   rules at the names that already exist. Where the mock needed a container the live markup
   had no element for (the credits card's two columns, the run row's stacked middle
   column), a wrapper div was added in the script and it carries a new name; nothing was
   renamed.

   LIGHT ONLY, and no toggle. The mocks ship a theme button; production does not, for the
   reason in redesign.css's head note, and these pages keep
   <meta name="color-scheme" content="light">.

   FONTS ARE SELF-HOSTED via redesign.css's @font-face block. Do not add a
   fonts.googleapis.com link: the site's CSP is `style-src 'self' 'unsafe-inline';
   font-src 'self' data:`, so it would be blocked, quietly.

   NO EM DASHES in anything a reader sees, and no new claims: every sentence on these three
   pages is the one the live page already carried, bar the three owner-approved changes
   recorded at their call sites in login.js and account.js (2026-09-29). */

/* `hidden` LOSES TO ANY `display` RULE, and these pages hide a lot with it: #acctMain,
   #acctPrefs, #acctGate, #runsMain, #runsGate, #signUpCard. Several of them are also grids
   or flex rows, so the attribute would do nothing and the element would sit there. One
   `!important` at the top of the sheet is the rule the mocks use and the cheapest way to
   make the attribute mean what it says. Anything hidden by CLASS instead (.terms-gate) is
   unaffected. */
[hidden] { display: none !important; }

/* ---- small shared pieces the text-page sheet scopes under .doc ----
   redesign.css styles `.doc .pmute` and `.doc .lead`, because on a text page those only
   ever appear inside the reading column. Here they are page furniture, so they need the
   unscoped rule. */
.pmute { color: var(--muted); font-size: 13.5px; line-height: 1.55; }
.pmute a { color: var(--accent); }
.lead { color: var(--muted); font-size: 17px; line-height: 1.55; max-width: 60ch; margin-top: 8px; }

/* ---- cards ----------------------------------------------------------------
   One measure for every panel on these three pages, so the login card, an account card and
   a run row are recognisably the same object. */
.card-box,
.acct-page,
#acctProfile,
.run-row {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-l);
  padding: 18px 20px; min-width: 0;
}
.card-box h3, .acct-record-hd { font-family: var(--f-display); font-size: 17px; font-weight: 600; letter-spacing: -.02em; margin: 0; }

/* ---- buttons ---------------------------------------------------------------
   The scripts emit five button classes that predate the redesign (.pbtn, .scta-primary,
   .scta-secondary, .scta-danger, .run-act). They are all pointed at redesign.css's `.btn`
   shape here rather than renamed, for the reason in the head note. */
.pbtn, .scta-primary, .scta-secondary, .scta-danger {
  --b-bg: var(--surface); --b-fg: var(--ink); --b-bd: var(--line);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 20px; border-radius: 999px;
  background: var(--b-bg); color: var(--b-fg); border: 1px solid var(--b-bd);
  font: 600 15px/1 var(--f-body); text-decoration: none; cursor: pointer;
  transition: background .15s, border-color .15s, transform .15s;
  white-space: nowrap;
}
.pbtn:hover, .scta-secondary:hover { border-color: var(--muted); color: var(--b-fg); }
.pbtn:active, .scta-primary:active, .scta-secondary:active, .scta-danger:active { transform: translateY(1px); }
.pbtn.primary, .scta-primary { --b-bg: var(--accent); --b-fg: var(--accent-ink); --b-bd: var(--accent); }
.pbtn.primary:hover, .scta-primary:hover { --b-bg: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-ink); }
.pbtn.ghost { --b-bg: transparent; --b-bd: var(--line); }
.pbtn.ghost:hover { --b-bg: var(--sunk); }
/* Destructive, in the warn tone rather than a second red: the palette has one warning
   colour and the deletion controls are the loudest thing that uses it. */
.scta-danger { --b-bg: var(--warn); --b-fg: var(--surface); --b-bd: var(--warn); }
.scta-danger:hover:not([disabled]) { filter: brightness(1.06); border-color: var(--warn); }
.pbtn[disabled], .scta-primary[disabled], .scta-secondary[disabled], .scta-danger[disabled] {
  opacity: .45; cursor: not-allowed;
}
.pbtn[hidden] { display: none; }
/* The compact size the run rows and the record use. */
.run-act, .acct-copy, .acct-del-cancel, .acct-del-go, .acct-signout {
  min-height: 36px; padding: 0 14px; font-size: 14px;
}

/* ============================ login.html ============================ */

.auth {
  display: grid; grid-template-columns: minmax(0, 448px) minmax(0, 292px);
  gap: clamp(24px, 4vw, 56px); justify-content: center; align-items: start;
  padding-block: clamp(24px, 6vw, 64px) 24px;
}
@media (max-width: 920px) { .auth { grid-template-columns: minmax(0, 448px); } }

/* ONE CARD WITH SEVERAL STATES INSIDE IT. The live page ships four sibling blocks
   (#termsGate, #authPending, #authCard, #signUpCard) and shows one at a time; the design is
   a single card whose contents change. So the four keep their ids and their `.card-box`
   class -- both of which tests read -- and the card shell is the element around them. */
.auth-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-l);
  box-shadow: var(--shadow); padding: clamp(20px, 5vw, 32px); min-width: 0;
}
.auth-card .card-box { background: none; border: 0; border-radius: 0; padding: 0; }
.auth-card .card-box h3 { font-size: clamp(24px, 3.2vw, 30px); letter-spacing: -.03em; margin-bottom: 10px; }
.auth-card .card-box > p:first-of-type { margin-top: 0; }
.auth-card .pbtn { width: 100%; margin-top: 4px; }
.card-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 24px; }
.card-top .brand img { width: 26px; height: 26px; }
.card-top .brand-word { font-size: 18px; }

/* the segmented switch between the two doors */
.seg { display: inline-flex; padding: 3px; background: var(--sunk); border-radius: 999px; max-width: 100%; }
.seg button {
  all: unset; padding: 7px 16px; border-radius: 999px;
  font: 500 14px/1 var(--f-body); color: var(--muted); cursor: pointer; text-align: center;
}
.seg button[aria-pressed="true"] { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgb(0 0 0 / .08); }
.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.auth-lede { color: var(--muted); font-size: 14.5px; line-height: 1.55; margin: 0 0 14px; }
/* the free-credit line on the sign-up door */
.offer {
  background: var(--accent-soft); border-radius: var(--r-m); padding: 14px 16px;
  font-size: 15px; line-height: 1.5; margin: 0 0 14px;
}
.offer b { color: var(--accent); font-weight: 600; display: block; font-size: 16.5px; }
.offer span { color: var(--muted); }
#authNote, #termsNote { margin: 14px 0 0; }

/* the recorded terms acceptance. HIDDEN BY A CLASS, never by the `hidden` attribute: this
   rule sets `display`, so the attribute would lose to it -- which is exactly the trap
   styles.css records beside the same pair. */
.terms-gate { display: none; }
.terms-gate.show { display: block; }
.terms-check {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 10px; align-items: start;
  font-size: 14.5px; line-height: 1.5; cursor: pointer; margin: 14px 0 0;
}
.terms-gate .pbtn { margin-top: 14px; }
.terms-check input { width: 18px; height: 18px; margin: 2px 0 0; accent-color: var(--accent); }

/* THE PRE-PAINT AUTH SWAP (the sign-in flash, 2026-09-11). auth_boot.js stamps
   data-auth-pending on <html> before the body is parsed; these rules pick the waiting card
   on the FIRST paint, and login.js drops the attribute when it knows the answer. Both
   signed-out cards are listed, because both are signed-out furniture: leaving one out is
   how the flash came back the first time. Done on an attribute rather than `hidden` for
   the reason above .terms-gate. */
html[data-auth-pending] #authCard,
html[data-auth-pending] #signUpCard { display: none; }
html[data-auth-pending] .seg { display: none; }
.auth-pending { display: none; }
html[data-auth-pending] .auth-pending { display: block; }
.spin {
  display: inline-block; width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: vo-spin .7s linear infinite; vertical-align: -2px; margin-right: 8px;
}
@keyframes vo-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spin { animation: none; } }

/* the quiet supporting column. NOT `.side`, which redesign.css already owns as the text
   pages' sticky contents rail (grid-column 2, position sticky). */
.auth-side { display: grid; gap: 18px; padding-top: 6px; }
.auth-facts { display: grid; gap: 16px; margin: 0; padding: 0; list-style: none; }
.auth-facts li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 10px; align-items: start; font-size: 14px; }
.auth-facts svg { color: var(--accent); margin-top: 3px; flex: none; }
.auth-facts b { font-weight: 600; display: block; }
.auth-facts p { color: var(--muted); font-size: 13.5px; margin-top: 3px; }
@media (max-width: 920px) { .auth-side { border-top: 1px solid var(--line); padding-top: 24px; } }

/* ============================ account.html ============================ */

.acct-col { max-width: 900px; }
.acct-head { padding-top: 40px; }
.acct-head h1 { font-size: clamp(30px, 3.4vw, 40px); }
.acct-cards { display: grid; gap: 14px; margin: 26px 0 40px; }
#acctMain { display: grid; gap: 14px; }

/* The gate callouts come from redesign.css's `.ruo-callout`, which carries the bottom margin
   a text page wants between it and the prose below. Here they are the only thing in their
   slot, and the grid gap is the spacing. */
#acctGate .ruo-callout, #runsGate .ruo-callout { margin: 0; }

/* The identity line. The design puts a Sign out button on its right-hand end; this page
   does not ship one -- the reason is at profileHTML() in account.js, and it is the
   destruction record, not the layout. The row keeps the flex measure so the day an endpoint
   gives this card a second thing to say, it has somewhere to put it. */
.acct-ident {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
  justify-content: space-between; font-size: 15px; color: var(--muted); margin: 0;
}
.acct-ident b { color: var(--ink); font-weight: 600; overflow-wrap: anywhere; }
.acct-ident .acct-who-line { min-width: 0; overflow-wrap: anywhere; }
.acct-ident .nowrap { white-space: nowrap; }
.acct-sub, .acct-note { margin: 6px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--muted); max-width: 70ch; }
.acct-sub b { color: var(--ink); }
.acct-note { margin-top: 10px; }
.acct-err { margin: 8px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--warn); max-width: 70ch; }
.acct-pending { color: var(--muted); font-weight: 500; }

/* THE BALANCE CARD: the figure on the left, the way to add to it on the right. Two
   wrappers account.js emits (.acct-bal, .acct-buy) rather than a grid-placement trick over
   five loose children, which would silently reshuffle the day a row is added. */
#acctCredits { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 300px); gap: 20px 32px; align-items: start; }
@media (max-width: 720px) { #acctCredits { grid-template-columns: minmax(0, 1fr); } }
.acct-bal { min-width: 0; }
.acct-buy { display: grid; gap: 0; }
.acct-buy .scta-primary, .acct-buy .pbtn { width: 100%; }
/* The balance row becomes a label above a big figure. MONEY IS NEVER MONOSPACE here (owner,
   2026-09-29): the figure is the display face with tabular digits, which lines the columns
   up without making a price look like a code. */
.acct-bal > .acct-row:first-child { display: block; border: 0; margin: 0; padding: 0; }
.acct-bal > .acct-row:first-child > span {
  font: 500 12px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); display: inline-flex; align-items: center; gap: 8px;
}
.acct-bal > .acct-row:first-child > span::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
.acct-bal > .acct-row:first-child > b {
  display: block; margin-top: 8px; font: 600 clamp(36px, 5vw, 48px)/1 var(--f-display);
  letter-spacing: -.03em; font-variant-numeric: tabular-nums;
}
.acct-bal > .acct-row:first-child > b .acct-pending { font-size: .58em; font-weight: 500; letter-spacing: -.01em; }
/* the free / purchased split, in the text face with tabular digits */
.acct-bal > .acct-sub { margin-top: 12px; font-size: 14px; line-height: 1.7; font-variant-numeric: tabular-nums; }
.acct-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line);
  font-size: 14px; font-variant-numeric: tabular-nums;
}
.acct-row > span { color: var(--muted); }
.acct-row > b { font: 600 16px/1.4 var(--f-body); font-variant-numeric: tabular-nums; }
.acct-row > b.acct-pending { font-weight: 500; }

/* the email preference */
.acct-check { display: flex; align-items: flex-start; gap: 10px; font-size: 15px; cursor: pointer; }
.acct-check input { margin: 4px 0 0; flex: none; width: 17px; height: 17px; accent-color: var(--accent); }

/* deleting the account, and the record it ends in */
.acct-del-idle { margin: 0; }
.acct-danger { border-left: 3px solid var(--warn); }
.acct-record { border-left: 3px solid var(--accent); }
.acct-resid { margin: 8px 0 0; padding-left: 20px; font-size: 13.5px; line-height: 1.6; color: var(--ink); }
.acct-resid li { margin-bottom: 6px; }
.acct-resid .acct-sub { display: inline; margin: 0; }
.acct-del-input {
  width: 100%; max-width: 340px; margin-top: 8px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: var(--r-s); background: var(--bg);
  color: var(--ink); font: 500 14px var(--f-mono);
}
.acct-del-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.acct-record-stmt {
  margin-top: 12px; padding-left: 12px; border-left: 2px solid var(--line);
  font-size: 13.5px; line-height: 1.6; color: var(--muted);
}
.acct-record .acct-row > b { font-size: 13.5px; overflow-wrap: anywhere; text-align: right; }
.acct-copied { font-size: 13px; color: var(--muted); }
/* The delete / keep pair and the copy button sit on a row of their own. */
.acct-danger .acct-note, .acct-record .acct-note { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
@media (max-width: 600px) { .acct-danger .acct-note .scta-danger, .acct-danger .acct-note .scta-secondary { flex: 1 1 auto; } }

/* ============================== runs.html ============================== */

.runs-head { padding-top: 40px; }
.runs-head h1 { font-size: clamp(30px, 3.4vw, 40px); }
.runs-count { font: 500 18px/1 var(--f-mono); color: var(--muted); letter-spacing: 0; margin-left: 8px; }
.runs-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-top: 28px; padding: 10px 0; border-bottom: 1px solid var(--line);
}
.runs-live { flex: 1 1 auto; }
.runs-refresh { margin-left: auto; }
#runsList { display: grid; gap: 12px; margin-top: 16px; padding-bottom: 40px; }
.runs-empty {
  padding: 40px 20px; text-align: center; color: var(--muted);
  border: 1.5px dashed var(--line); border-radius: var(--r-l);
}
.runs-empty--err { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.runs-expiry { margin: 4px 0 0; }

.run-row { display: grid; gap: 14px; }
/* THE ROW: status, what it was, what it cost. Three columns, and the middle one stacks the
   file name over "reference - when" -- which is why runs.js wraps those three spans in
   .run-main / .run-meta instead of leaving five siblings to be placed by hand. */
.run-line { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 6px 16px; align-items: center; }
.run-status {
  display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px;
  font: 500 12px/1.5 var(--f-mono); white-space: nowrap; background: var(--sunk); color: var(--muted);
}
.run-status::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.run-status--ok { background: var(--accent-soft); color: var(--accent); }
.run-status--live { background: color-mix(in srgb, var(--violet) 14%, transparent); color: var(--violet); }
.run-status--live::before { animation: vo-pulse 1.2s ease-in-out infinite; }
.run-status--bad { background: var(--warn-soft); color: var(--warn); }
@keyframes vo-pulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .run-status--live::before { animation: none; } }
.run-main { min-width: 0; }
.run-file { display: block; font-weight: 600; overflow-wrap: anywhere; }
.run-meta { display: block; font-size: 13.5px; color: var(--muted); }
/* The separator between the reference and the age, in CSS so an absent value cannot leave a
   stray middle dot behind.
   The spacing is a MARGIN and not two spaces inside `content`: white space at the edge of
   generated content collapses against the inline box beside it, which printed
   "Skin ·9/18/2026" -- a gap on one side and none on the other. */
.run-ref:not(:empty) + .run-when:not(:empty)::before { content: "\00b7"; margin-inline: 6px; }
.run-price { font: 600 15px/1.4 var(--f-body); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; grid-column: 3; }
.run-foot { display: grid; gap: 8px; font-size: 13px; color: var(--muted); }
.run-foot p { margin: 0; }
.run-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.run-del:hover { color: var(--warn); border-color: var(--warn); }
.run-del-err { align-self: center; font-size: 13px; color: var(--warn); }
.run-id code { font-family: var(--f-mono); font-size: 12px; overflow-wrap: anywhere; }
.run-why { color: var(--ink); overflow-wrap: anywhere; }
@media (max-width: 600px) {
  .run-line { grid-template-columns: auto minmax(0, 1fr); }
  .run-price { grid-column: 1 / -1; text-align: left; }
  .run-actions .run-act { flex: 1 1 auto; }
}

/* ======================= 404.html and the retired stubs ======================= */

.err-page { padding-block: clamp(48px, 10vw, 96px) 24px; text-align: center; }
.err-page h1 { font-size: clamp(30px, 3.7vw, 44px); margin: 16px 0 12px; }
.err-page .lead { margin: 0 auto; max-width: 54ch; }
.err-acts { margin-top: 26px; display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
.err-acts .ghost { align-self: center; color: var(--accent); text-decoration: none; font-weight: 600; }
.err-acts .ghost:hover { text-decoration: underline; }
.alpha-pill {
  display: inline-block; font: 500 12px/1 var(--f-mono); letter-spacing: .08em;
  color: var(--accent); background: var(--accent-soft); border-radius: 999px; padding: 6px 12px;
}
/* The two retired URLs meta-refresh away in zero seconds, so this is what a reader sees
   only when the refresh is blocked. It is one sentence; it gets a readable line and
   nothing else. */
.stub { padding-block: clamp(48px, 10vw, 96px) 24px; }
.stub p { max-width: 62ch; font-size: 16.5px; line-height: 1.75; }
