/* ═══════════════════════════════════════════════════════════════════════════
   gw-system.css — THE PRIMITIVES. Loaded LAST, after sentinel.css and
   gw-editorial.css, because its whole job is to end arguments those two sheets
   have been having with each other.

   WS1 · 2026-08-11.

   gw-tokens.css declares the scale. This file is what SPENDS it. Until now the
   token layer resolved on every route and almost nothing painted from it, so
   the system was real in the diff and invisible on the page.

   Measured in Chromium against the rendered routes before this file existed:

     22 distinct filled-button treatments across 16 public routes — six fills,
        five radii, four weights, heights from 33px to 100px. The same label,
        "Run a free scan", shipped in three polarities: light-on-dark on `/`
        and `/oneshot`, a different light-on-dark on `/how-it-works`, and the
        INVERSE on `/register` and `/login`.
     4  distinct <h1> treatments.
     13 border radii on five pages against a four-token scale.

   ── WHY THE PRIMARY BUTTON READS ITS OWN COLOURS FROM THE WRAPPER ──────────

   DESIGN.md §7.1 specifies one primary object, byte-identical everywhere:
   `background:#F7F6F4; color:#111316` — 17.0:1, the strongest pair in §3.2.
   Taken literally that is a light button, and it is invisible on this site's
   editorial pages, whose ground is #F7F6F4 paper.

   The site runs two themes side by side in one document (DESIGN_AMENDMENTS.md
   A-1), so the primary needs both polarities and they must never be chosen by
   hand — that is precisely how 22 treatments happened. Both halves come from
   the wrapper's own theme instead:

       background: var(--gw-sys-color-text-high)
       color:      var(--gw-sys-color-on-high)

   which resolves to #111316 on #F7F6F4 ink on the paper estate and #F7F6F4 on
   #08090A on the near-black estate. Both are 17.0:1. It is ONE object with one
   rule; the polarity is a property of the surface it is standing on, and no
   call site gets a vote.

   ── HOVER IS NOT AN OPACITY FADE ─────────────────────────────────────────

   The sitewide `:hover{opacity:.88}` dropped the primary CTA from 17.98:1 to
   13.87:1 — a 23% loss of separation — and fading a control on hover is the
   visual grammar of DISABLING it (F-086). Per-role instead, and the fill moves
   further from the ground rather than closer: `--gw-sys-color-cta-hi` is
   #000000 on paper and #FFFFFF on near-black, declared per theme in
   gw-tokens.css beside the pair it belongs to.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · BUTTONS ─────────────────────────────────────────────────────────────
   The selector list is long on purpose. It is the census: every filled control
   that measured as a primary CTA on a public route, named, so that the count
   is 2 and the list is the evidence. A new CTA class that is not in here is a
   twenty-third treatment, and test_button_primitive_is_one_object.py says so.
   ─────────────────────────────────────────────────────────────────────────── */
/* On the self-chained selectors (`.vs-btn.vs-btn`): several of these classes
   are styled from a per-page <style> block that the head emits AFTER this
   file, so equal specificity loses on source order alone. Repeating the class
   raises specificity without reaching for !important — which in a primitives
   sheet would make every legitimate per-instance override impossible too. */
.gw-btn.gw-btn,
.gwe .btn,
.gwe .btn.lg,
.sx .btn,
.sx .btn.lg,
.masthead .mcta,
.enb-btn.enb-btn,
.vs-btn.vs-btn,
.gwsb-btn.gwsb-btn,
.supcta.supcta,
.xs-buy.xs-buy,
.sx a.go.go,
.gwe a.go.go {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  box-sizing: border-box;
  min-height: var(--gw-sys-tap-min, 44px);
  /* The vertical padding is REAL, not zero, and min-height is the floor under
     it rather than the only thing holding the button open. `padding: 0 24px`
     with `line-height: 1` gives a 15px label a 15px content box and lets
     min-height do all the work — which is fine for exactly as long as every
     label fits on one line. The moment one wraps (a longer locale, a narrow
     card, a 320px phone) the content box is 30px, min-height stops binding,
     and the two lines sit flush against the top and bottom edges of the fill.
     8px top and bottom costs NOTHING at rest: 15px label + 16px padding + 2px
     border = 33px, still under the 44px floor, so every button on the site is
     the same height it was, to the pixel. It only starts paying when the label
     wraps, which is the case it is here for. */
  padding: var(--gw-sys-space-8, 8px) var(--gw-sys-space-24, 24px);
  border: 1px solid transparent;
  border-radius: var(--gw-sys-radius-md, 6px);
  font-family: var(--gw-sys-type-font-sans);
  font-size: var(--gw-sys-type-body-s-size, .9375rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  box-shadow: none;
  background-image: none;
  transition:
    background-color var(--gw-sys-dur-micro, 140ms) var(--gw-sys-ease-out, cubic-bezier(.2,0,0,1)),
    color            var(--gw-sys-dur-micro, 140ms) var(--gw-sys-ease-out, cubic-bezier(.2,0,0,1)),
    border-color     var(--gw-sys-dur-micro, 140ms) var(--gw-sys-ease-out, cubic-bezier(.2,0,0,1)),
    box-shadow       var(--gw-sys-dur-micro, 140ms) var(--gw-sys-ease-out, cubic-bezier(.2,0,0,1));
}

/* PRIMARY — one object. 17.0:1 in both polarities, by construction. */
.gw-btn-primary.gw-btn-primary,
.gwe .btn,
.gwe .btn.lg,
.sx .btn,
.sx .btn.lg,
.masthead .mcta,
.enb-btn.enb-btn-primary,
.vs-btn.vs-btn.primary,
.gwsb-btn.gwsb-btn,
.supcta.supcta,
.xs-buy.xs-buy,
.sx a.go.go,
.gwe a.go.go {
  background-color: var(--gw-sys-color-text-high, #F7F6F4);
  color: var(--gw-sys-color-on-high, #111316);
  border-color: var(--gw-sys-color-text-high, #F7F6F4);
}

/* SECONDARY — the only other treatment there is. */
.gw-btn-secondary.gw-btn-secondary,
.gwe .btn.ghost,
.gwe .btn.bare,
.sx .btn.ghost,
.enb-btn.enb-btn-secondary,
.vs-btn.vs-btn.secondary {
  background-color: transparent;
  background-image: none;
  color: var(--gw-sys-color-text-high, #F7F6F4);
  border: 1px solid var(--gw-sys-color-line-strong, #5D6773);
  box-shadow: none;
}

.gw-btn-primary.gw-btn-primary:hover,
.gwe .btn:hover,
.sx .btn:hover,
.masthead .mcta:hover,
.enb-btn.enb-btn-primary:hover,
.vs-btn.vs-btn.primary:hover,
.gwsb-btn.gwsb-btn:hover,
.supcta.supcta:hover,
.xs-buy.xs-buy:hover,
.sx a.go.go:hover,
.gwe a.go.go:hover {
  /* NOT opacity. The fill moves AWAY from the ground and gains one shadow. */
  opacity: 1;
  background-color: var(--gw-sys-color-cta-hi, #FFFFFF);
  border-color: var(--gw-sys-color-cta-hi, #FFFFFF);
  box-shadow: var(--gw-sys-shadow-1, 0 1px 2px rgba(0,0,0,.28));
  text-decoration: none;
}

.gw-btn-secondary.gw-btn-secondary:hover,
.gwe .btn.ghost:hover,
.gwe .btn.bare:hover,
.sx .btn.ghost:hover,
.enb-btn.enb-btn-secondary:hover,
.vs-btn.vs-btn.secondary:hover {
  opacity: 1;
  background-color: transparent;
  border-color: var(--gw-sys-color-signal, #AF5125);
}

/* The secondary must win over the primary block above where a control carries
   both classes; the ghost/bare modifiers are more specific than `.gwe .btn`
   only by one class, so state it after and let source order settle it. */
.gwe .btn.ghost,
.gwe .btn.bare,
.sx .btn.ghost {
  background-color: transparent;
  color: var(--gw-sys-color-text-high, #F7F6F4);
  border-color: var(--gw-sys-color-line-strong, #5D6773);
}

.gw-btn[disabled],
.gwe .btn[disabled],
.sx .btn[disabled] {
  opacity: .65;
  cursor: default;
  box-shadow: none;
}

/* `.lg` was a fifth height (50px) and a sixth radius. A larger button is a
   layout decision — more horizontal padding — not a different treatment. */
.gwe .btn.lg, .sx .btn.lg {
  min-height: var(--gw-sys-tap-min, 44px);
  /* Same vertical padding as the base rule, for the same wrapped-label
     reason — this rule re-states `padding` in full, so omitting it here
     would put the zero back on every .lg button. */
  padding: var(--gw-sys-space-8, 8px) var(--gw-sys-space-32, 32px);
}

/* Width is a layout decision, not a treatment: `.block` keeps its job and
   changes nothing else. */
.gw-btn.block,
.gwe .btn.block,
.sx .btn.block {
  display: flex;
  width: 100%;
}

/* ── 2 · ONE H1 TREATMENT ────────────────────────────────────────────────────
   THE EXCEPTION WAS THE DEFECT. The first pass wrote the rule below and then
   carved out "the hero H1 is the ONE exception, a different STEP of the same
   scale". Re-measured across 22 rendered routes at 1440 afterwards, that one
   exception had become NINE distinct <h1> treatments — six sizes (32 / 38.4 /
   42 / 56 / 72 / 76px), three weights (300 / 350 / 800) and four inks — because
   every page-level <style> block that already disagreed simply kept
   disagreeing, and a documented exception is an invitation to a tenth.

   So: one step, one weight, one leading, one tracking, one face, on every
   <h1> on the property. No hero exception, no secondary-page exception.

   WHY d1 AND NOT THE `h1` STEP. Both were live. d1 (76px→40px, weight 300)
   already painted on 7 of 22 routes INCLUDING the whole money path, whose
   hero is under rebuild in a parallel workstream and is pinned to it; the
   `h1` step (42px, weight 350) painted on 8. Collapsing down to 42px would
   have shrunk the money path's largest element to settle an argument on
   /terms, and would have had to reach into a locked file to do it.
   Collapsing UP is the change a reader can see: the legal, support and
   auth pages now open with the same masthead the money path opens with,
   which is the whole point of a shell.

   `--gw-sys-type-h1-*` is NOT retired — `.gwe .oprice` and the section
   -opening statements still spend it. It is simply no longer what an <h1>
   resolves to. One role, one step, and the token table still has ten.
   ─────────────────────────────────────────────────────────────────────────── */
h1,
.sx h1,
.sx-app h1,
.gwe h1 {
  font-family: var(--gw-sys-type-font-display);
  font-size: var(--gw-sys-type-d1-size, clamp(2.5rem, 1.6rem + 4.2vw, 4.75rem));
  font-weight: var(--gw-sys-type-d1-weight, 300);
  line-height: var(--gw-sys-type-d1-leading, 1.02);
  letter-spacing: var(--gw-sys-type-d1-tracking, -0.03em);
  max-width: 18ch;
  text-wrap: balance;
  overflow-wrap: break-word;
}

/* The same declarations again, at a specificity these call sites cannot beat.
   Every selector here was MEASURED overriding the rule above on a rendered
   route, and each is either in a file another workstream owns (sx_register.py,
   sx_oneshot_editorial.py) or in a per-page <style> block the head emits after
   this file, where equal specificity loses on source order alone. The list is
   the evidence: a tenth treatment shows up as a name that is not on it.
   test_one_h1_treatment.py renders all 22 routes and fails on a second tuple. */
.sx h1.regh.regh,
.sx .authcard.authcard h1,
.hiw.hiw h1,
.gwe .bi-hero-tight.bi-hero-tight h1,
.enb-hero.enb-hero h1,
.gwe.abt .herodark.herodark h1,
.legal-header.legal-header h1,
.terms-header.terms-header h1,
.sx h1.hero.hero,
.gwe .herodark.herodark h1,
.gwe .hero.hero h1,
.vl-hero.vl-hero h1 {
  font-family: var(--gw-sys-type-font-display);
  font-size: var(--gw-sys-type-d1-size, clamp(2.5rem, 1.6rem + 4.2vw, 4.75rem));
  font-weight: var(--gw-sys-type-d1-weight, 300);
  line-height: var(--gw-sys-type-d1-leading, 1.02);
  letter-spacing: var(--gw-sys-type-d1-tracking, -0.03em);
}

/* ── 3 · RADII — four values, and nothing between them ───────────────────────
   13 painted on five pages: 5 6 7 8 9 10 12 14 18 999. Values one pixel apart
   are not decisions. The card grids were the worst of it: /oneshot rendered 27
   cards in nine padding+radius combinations, several ASYMMETRIC (18/18/16/18)
   because two rem values rounded to different pixels on opposite edges of one
   box. Cards in the same grid were visibly not the same card.
   ─────────────────────────────────────────────────────────────────────────── */
.gwe .btn, .sx .btn, .sx .btn.lg, .masthead .mcta, .gw-btn.gw-btn,
.enb-btn.enb-btn, .vs-btn.vs-btn, .gwsb-btn.gwsb-btn, .supcta.supcta {
  border-radius: var(--gw-sys-radius-md, 6px);
}
/* A pill is a CHIP. `.xs-buy` and `.go` were pill-shaped buttons, which is a
   third button shape wearing a chip's costume — both are radius-md now. */
.xs-buy.xs-buy, .sx a.go.go, .gwe a.go.go {
  border-radius: var(--gw-sys-radius-md, 6px);
}
.badge, .chip, .pill, .gwe .badge, .sx .tag, .sx-app .tag, .sx .chip {
  border-radius: var(--gw-sys-radius-pill, 999px);
}
.gwe .ocard, .gwe .pcell, .gwe .outcome, .gwe .rev, .gwe .tl .tstep,
.sx .card, .sx .blogcard, .sx .authcard, .sx-app .card, .gwe-card {
  border-radius: var(--gw-sys-radius-lg, 12px);
}
.gwe input, .gwe select, .gwe textarea,
.sx input, .sx select, .sx textarea,
.sx .fld, .sx-app .fld {
  border-radius: var(--gw-sys-radius-sm, 4px);
}

/* ── 4 · THE CARD PRIMITIVE ──────────────────────────────────────────────────
   DESIGN.md §7.2. One padding tuple, one radius, one surface, one hairline —
   available to anything that wants to stop inventing a card. */
.gwe-card {
  box-sizing: border-box;
  padding: var(--gw-sys-space-16, 16px);
  border-radius: var(--gw-sys-radius-lg, 12px);
  background: var(--gw-sys-color-raised, #14171C);
  border: 1px solid var(--gw-sys-color-line, #2E3338);
}
.gwe-card:hover {
  border-color: var(--gw-sys-color-line-strong, #5D6773);
}

/* ── 5 · MOTION — the four durations, and no `transition: all` ───────────── */
@media (prefers-reduced-motion: reduce) {
  .gw-btn, .gwe .btn, .sx .btn, .masthead .mcta, .gwe-card {
    transition-duration: .01ms !important;
  }
}
