/* ============================================================================
   Qwerty Tweaks marketing site

   The tokens are the app's own (src/renderer/styles.css, the
   html[data-theme='dark'] block). The site and the app are one product, so they
   are built from the same material rather than from a second, invented palette:

     - one functional accent. The app's comment says the icon mark is "a dusty
       #c2849e" and that it deepened that to a single accent, because "a
       decoration pink beside a functional pink is two accents arguing". The
       site follows that rule; the dusty rose survives only inside the icon.
     - --accent-ink is the ink used on ANY filled surface. The primary button and
       the numerals inside the risk bar both use it, because there is only one
       such ink and inventing a second would be two inks arguing.
     - three text levels, a radius ladder rather than one radius, a 4px space
       grid, and sizes and line heights held in tokens so no rule can quietly
       introduce a sixteenth font size.
     - no monospace face, for the same reason the app has none: numbers that must
       not jitter get font-variant-numeric, not a second typeface. The SHA-256
       line below is the place that rule is most tempted, and it holds.

   Elevation has exactly two users and both are images of another window. Nothing
   else on the page floats.

   THE TYPEFACE is IBM Plex Sans, self-hosted from site/assets/fonts (latin
   subset, variable 100-700, SIL Open Font Licence in OFL.txt beside it). The app
   still sets its display text in Roboto Slab; this is the newer face the app can
   adopt later. Self-hosting it means the page makes zero third-party requests.
   ========================================================================== */

@font-face {
  font-family: 'IBM Plex Sans';
  src: url('assets/fonts/ibm-plex-sans-var.woff2') format('woff2');
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Surfaces */
  --bg:         #17171a;
  --surface:    #1e1e22;
  --surface-2:  #26262b;

  /* Lines */
  --border:        #33333a;
  --border-strong: #46464f;

  /* Text - three levels */
  --text:   #f2f2f4;
  --text-2: #b8b8c0;
  --text-3: #9a9aa5;

  /* The one accent, and the ink that sits on a filled surface */
  --accent:       #e0568f;
  --accent-hover: #e8639a;
  --accent-ink:   #17171a;

  /* Status - these carry meaning (safe / moderate / dangerous), so they keep
     their colour. Deliberately not monochrome. */
  --ok:   #5cba77;
  --warn: #d8a63f;
  --bad:  #e8736c;

  /* Radii - a ladder by role. Controls are 8px, a figure is 12px, and the one
     panel on the page is 16px. */
  --r-sm: 8px;
  --r-lg: 12px;
  --r-xl: 16px;

  /* Space - the app's 4px grid, plus --s-9: a web page breathes in bigger steps
     than a desktop window does, where the ladder stops at --s-8. */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 18px;
  --s-5: 26px; --s-6: 34px; --s-7: 44px; --s-8: 56px;
  --s-9: 72px;

  /* Elevation - used twice, by the two images of another window. */
  --shadow-1: 0 1px 2px rgba(0,0,0,.30), 0 12px 28px rgba(0,0,0,.34);
  --shadow-2: 0 2px 6px rgba(0,0,0,.34), 0 18px 40px rgba(0,0,0,.46);

  /* Motion - interaction only, never on load. --t-press is shorter than the
     hover step because a press must feel like it happened, not like it eased. */
  --ease:    cubic-bezier(.2, 0, .2, 1);
  --t-hover: 180ms;
  --t-state: 140ms;
  --t-press: 90ms;

  /* Type scale. In rem, so a reader who has raised their browser's text size
     gets a bigger page rather than a broken one. Five steps are the app's own
     (xs through xl); the page adds the two above them. */
  --fs-xs: .75rem;    /* 12px - metadata: file names, column heads, state lines */
  --fs-sm: .8125rem;  /* 13px - supporting: descriptions, captions */
  --fs-md: .9375rem;  /* 15px - body */
  --fs-lg: 1.0625rem; /* 17px - subsection titles, the lede, the quote */
  --fs-xl: 1.25rem;   /* 20px - the numbers in the spec strip */
  --fs-2xl: 1.5rem;   /* 24px - section titles */
  --fs-display: clamp(2.125rem, 4.2vw, 3rem);  /* the page title */

  --lh-flat: 1;      /* single-line controls */
  --lh-display: 1.12;
  --lh-heading: 1.3;
  --lh-body: 1.62;
  --lh-meta: 1.45;

  /* Controls. The app's --control-h is 32px, tuned for a dense desktop window
     read at arm's length. A page read with a mouse and a scrolling wheel gets
     one step more, and touch gets the 44px below. */
  --control-h: 36px;
  --control-h-lg: 44px;

  /* Measures */
  --measure: 62ch;         /* a section's supporting paragraph */
  --measure-narrow: 46ch;  /* the lede, and a caption under a figure */

  /* How far a heading stops below the sticky masthead when an anchor is
     followed. Larger than the bar on purpose: a heading that lands just clear
     of the bar reads as though it were placed there. */
  --anchor-offset: 92px;

  --content-max: 1120px;   /* the app's own reading column */
  --font: 'IBM Plex Sans', 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* draw the browser's own chrome (scrollbars, form controls) for a dark page */
  color-scheme: dark;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-md);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}

/* An image carries its own proportions. The width/height attributes on a shot
   declare the raster's size so the page does not reflow as each one arrives;
   `height: auto` is what stops those same attributes from pinning the box too,
   which is how a 2560x1600 screenshot ends up squashed into whatever width the
   column happens to be. */
img { max-width: 100%; height: auto; display: block; }
h1, h2, h3 { margin: 0; font-weight: 600; }
h1 { font-size: var(--fs-display); line-height: var(--lh-display); letter-spacing: -.021em; text-wrap: balance; margin-bottom: var(--s-4); }
h2 { font-size: var(--fs-2xl); line-height: var(--lh-heading); letter-spacing: -.014em; margin-bottom: var(--s-3); }
h3 { font-size: var(--fs-lg); line-height: var(--lh-heading); letter-spacing: -.006em; margin-bottom: var(--s-1); }
p { margin: 0 0 var(--s-4); }

.width { width: min(100% - 40px, var(--content-max)); margin-inline: auto; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- focus ----------
   One ring, and each element says how far it sits from its own edge: a button
   inside a row must not paint over its neighbour. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn:focus-visible, .masthead-nav a:focus-visible, summary:focus-visible { outline-offset: 1px; }

.skip {
  position: absolute; left: var(--s-3); top: calc(-1 * var(--s-8)); z-index: 30;
  background: var(--accent); color: var(--accent-ink);
  padding: var(--s-3) var(--s-4); border-radius: var(--r-sm);
  font-size: var(--fs-sm); font-weight: 600; line-height: var(--lh-flat);
  text-decoration: none;
  transition: top var(--t-hover) var(--ease);
}
.skip:focus { top: var(--s-3); }

/* ---------- buttons: no gradient, no glow, no lift ----------
   Four states, and they are the app's four states said in the site's material:
   rest, hover, press (the only place a scale is allowed on this page), and
   disabled. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--control-h); padding: var(--s-2) var(--s-4);
  border: 1px solid transparent; border-radius: var(--r-sm);
  font-family: inherit; font-size: var(--fs-md); font-weight: 600;
  line-height: var(--lh-flat);
  text-decoration: none; cursor: pointer;
  transition: background-color var(--t-state) var(--ease),
              border-color var(--t-state) var(--ease),
              color var(--t-state) var(--ease),
              transform var(--t-press) var(--ease);
}
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-quiet { background: transparent; color: var(--text); border-color: var(--border-strong); }
.btn-quiet:hover { border-color: var(--text-3); background: var(--surface-2); }
.btn:active { transform: scale(.985); }
.btn-lg { min-height: var(--control-h-lg); padding: var(--s-3) var(--s-5); font-size: var(--fs-lg); }
/* Disabled is a state, not a missing style: it keeps its geometry, stops
   answering the pointer, and says so with the cursor. */
.btn:disabled { opacity: .5; cursor: not-allowed; background: var(--surface-2); border-color: var(--border-strong); color: var(--text-2); }
.btn:disabled:hover { background: var(--surface-2); }
.btn-primary:disabled { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* ============================ MASTHEAD ============================
   Solid, not frosted: a translucent bar over a dark page is decoration. Sticky
   because the page is long and this bar is the only wayfinding on it. */
.masthead {
  position: sticky; top: 0; z-index: 20;
  background: var(--bg); border-bottom: 1px solid var(--border);
}
.masthead-in {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-3) var(--s-5); min-height: 60px;
}
.mark { display: inline-flex; align-items: center; gap: var(--s-3); text-decoration: none; color: var(--text); }
.mark img { border-radius: var(--r-sm); }
.mark-name { font-weight: 600; font-size: var(--fs-md); letter-spacing: -.008em; }

.masthead-nav { display: flex; gap: var(--s-1); margin-left: auto; }
.masthead-nav a {
  display: inline-flex; align-items: center;
  min-height: var(--control-h); padding: 0 var(--s-3);
  border-radius: var(--r-sm); color: var(--text-2);
  font-size: var(--fs-sm); text-decoration: none;
  transition: background-color var(--t-state) var(--ease), color var(--t-state) var(--ease);
}
.masthead-nav a:hover { color: var(--text); background: var(--surface-2); }
/* Where you are: the app's own selected-nav treatment - one soft filled
   capsule with a hairline round it, no accent bar, no floating card. The
   hairline is the accent because this is the one place the page answers
   "where am I", and that is what the accent is for. */
.masthead-nav a[aria-current='true'] {
  background: var(--surface-2); color: var(--text); font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--accent);
}
.dl-link { min-height: var(--control-h); padding: 0 var(--s-4); }

/* Sections stop clear of the sticky bar when an anchor is followed. */
section[id] { scroll-margin-top: var(--anchor-offset); }

/* ============================ OPENING ============================
   The statement first, then the product itself, then the numbers. A reader
   should see what this is before they are asked to believe anything. */
.opening { padding: var(--s-9) 0 var(--s-7); }
.opening-copy { max-width: 44rem; }
.lede {
  color: var(--text-2); font-size: var(--fs-lg); line-height: var(--lh-body);
  max-width: var(--measure-narrow); margin-bottom: var(--s-6);
}
.opening-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-5); }
.opening-note { margin: 0; color: var(--text-3); font-size: var(--fs-sm); }

/* The app window. This is the one image above the fold. */
.app-window { margin: var(--s-7) 0 0; }
.app-window img { border: 1px solid var(--border); border-radius: var(--r-lg); box-shadow: var(--shadow-1); }

/* The specs, as a measured strip rather than a card: a row of numbers needs a
   line above it, not a container around it. */
.spec-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0 var(--s-5); margin: var(--s-7) 0 0; }
.spec { border-top: 1px solid var(--border); padding-top: var(--s-4); }
.spec dt { color: var(--text-3); font-size: var(--fs-xs); line-height: var(--lh-meta); margin-bottom: var(--s-2); }
.spec dd { margin: 0; font-size: var(--fs-xl); font-weight: 600; line-height: var(--lh-flat); font-variant-numeric: tabular-nums; }

/* ============================ BANDS ============================
   Surface steps create the rhythm between sections, so no section needs the
   same padding as its neighbour. */
.band { background: var(--surface); border-block: 1px solid var(--border); padding: var(--s-9) 0; }
.section-lede { color: var(--text-2); max-width: var(--measure); margin-bottom: var(--s-7); }

/* ============================ RISK LABELS ============================
   The signature: the app's own classification, drawn to scale and labelled in
   place, so the bar can be read without relying on colour. */
/* Square corners: this is a measured bar, not a badge. */
.riskbar { display: flex; height: 22px; margin-bottom: var(--s-7); }
.riskbar-seg {
  flex: var(--share);
  display: flex; align-items: center; justify-content: center;
  color: var(--accent-ink);
  font-size: var(--fs-xs); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.riskbar-seg + .riskbar-seg { border-inline-start: 2px solid var(--surface); }
.seg-safe { background: var(--ok); }
.seg-moderate { background: var(--warn); }
.seg-dangerous { background: var(--bad); }

.tiers { list-style: none; margin: 0; padding: 0; }
.tier {
  display: grid; align-items: baseline; gap: 0 var(--s-4);
  grid-template-columns: 8px minmax(6rem, auto) 3rem minmax(0, 1fr);
  padding: var(--s-4) 0; border-top: 1px solid var(--border);
}
.tier:first-child { border-top: 0; padding-top: 0; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-3); align-self: center; }
.dot.safe { background: var(--ok); }
.dot.moderate { background: var(--warn); }
.dot.dangerous { background: var(--bad); }
.tier-name { font-weight: 600; }
.tier-count { color: var(--text-2); font-variant-numeric: tabular-nums; }
.tier-meaning { color: var(--text-2); font-size: var(--fs-sm); }

/* ============================ CATALOGUE ============================
   Tabular information gets a table, not a wall of cards. */
.included { padding: var(--s-9) 0; }
.catalog { width: 100%; border-collapse: collapse; }
.catalog th, .catalog td { text-align: left; vertical-align: top; padding: var(--s-4) var(--s-4) var(--s-4) 0; border-bottom: 1px solid var(--border); }
.catalog td:last-child, .catalog th:last-child { padding-right: 0; }
.catalog thead th { font-size: var(--fs-xs); font-weight: 600; color: var(--text-3); border-bottom: 1px solid var(--border-strong); padding-top: 0; }
.catalog tbody th { font-weight: 400; }
.catalog .num { text-align: right; font-variant-numeric: tabular-nums; color: var(--text-2); white-space: nowrap; }
.catalog td:last-child { color: var(--text-3); font-size: var(--fs-sm); }
.page-name { display: block; font-weight: 600; font-size: var(--fs-md); }
.page-sub { display: block; color: var(--text-3); font-size: var(--fs-sm); line-height: var(--lh-meta); margin-top: 2px; }

.sub-head { margin: var(--s-9) 0 var(--s-2); }
.tools { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--s-7); margin: 0; }
.tools > div { padding: var(--s-4) 0; border-top: 1px solid var(--border); }
.tools dt { font-weight: 600; margin-bottom: var(--s-1); }
.tools dd { margin: 0; color: var(--text-2); font-size: var(--fs-sm); }

/* ============================ INTERFACE ============================
   One lead screenshot, then pairs: importance decides size. */
.shot { margin: 0; }
.shot img { width: 100%; height: auto; border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--bg); box-shadow: var(--shadow-2); }
.shot figcaption { margin-top: var(--s-3); color: var(--text-2); font-size: var(--fs-sm); line-height: var(--lh-meta); max-width: var(--measure-narrow); }
.shot figcaption b { display: block; color: var(--text); }
.shot-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-6); margin-top: var(--s-6); }

/* ============================ LIMITS ============================ */
.limits { padding: var(--s-8) 0 var(--s-9); }
.limits-grid { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, .8fr); gap: var(--s-8); align-items: start; }
.plain { list-style: none; margin: 0; padding: 0; }
.plain li { padding: var(--s-3) 0; border-top: 1px solid var(--border); color: var(--text-2); }
.plain li:first-child { border-top: 0; padding-top: 0; }
.quote { margin: 0; border-left: 3px solid var(--accent); padding-left: var(--s-5); }
.quote p { font-size: var(--fs-lg); line-height: var(--lh-heading); color: var(--text); margin-bottom: var(--s-3); }
.quote cite { font-style: normal; color: var(--text-3); font-size: var(--fs-xs); }

/* ============================ DOWNLOAD ============================
   Two option rows, not two cards: a row is what this content is. Each row
   carries a state, and the state is the app's own vocabulary - the row that
   cannot offer its file is dimmed and says why, exactly as a tweak the PC
   cannot run is dimmed and says why. */
.download { padding: var(--s-9) 0; }
.download-grid { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); gap: var(--s-8); align-items: start; }
.reqs { list-style: none; margin: 0 0 var(--s-4); padding: 0; }
.reqs li { padding: var(--s-2) 0; border-top: 1px solid var(--border); color: var(--text-2); font-size: var(--fs-sm); }
.reqs li:first-child { border-top: 0; padding-top: 0; }
.note { color: var(--text-3); font-size: var(--fs-sm); margin: var(--s-3) 0 0; }

.options { display: grid; }
.option {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s-5); align-items: start;
  padding: var(--s-5) 0; border-top: 1px solid var(--border);
}
.option:first-child { border-top: 0; padding-top: 0; }
.option-text p { color: var(--text-2); font-size: var(--fs-sm); margin-bottom: var(--s-2); }
.option-text h3 { margin-bottom: var(--s-2); }
/* Two classes, because `.option-text p` below sets the description's size and
   would otherwise win over this one and quietly turn metadata into body text. */
.option-text .file { margin: 0; color: var(--text-3); font-size: var(--fs-xs); line-height: var(--lh-meta); font-variant-numeric: tabular-nums; }
.option-action { display: flex; flex-direction: column; align-items: flex-end; gap: var(--s-2); }
.option-state {
  margin: 0; max-width: 34ch; text-align: right;
  color: var(--text-3); font-size: var(--fs-xs); line-height: var(--lh-meta);
}
/* The four working states, and the one that means nothing runs at all. */
.option[data-state='checking'] .option-state { color: var(--text-2); }
.option[data-state='ready'] .option-state { color: var(--text-2); }
.option[data-state='unavailable'] .option-text { opacity: .6; }
.option[data-state='unavailable'] .file { opacity: 1; }
.option[data-state='starting'] .btn { opacity: .55; }
.option[data-state='started'] .option-state { color: var(--ok); }
.option[data-state='failed'] .option-state { color: var(--bad); }

/* Verify: one panel, closed until asked. It holds the two hashes and the
   sentence about what Windows will say the first time. */
.verify {
  margin-top: var(--s-6);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-xl);
}
.verify summary {
  cursor: pointer; padding: var(--s-4) var(--s-5);
  font-size: var(--fs-md); font-weight: 600; line-height: var(--lh-flat);
}
.verify summary::marker { color: var(--text-3); }
.verify-body { padding: 0 var(--s-5) var(--s-5); }
.verify-body p { color: var(--text-2); font-size: var(--fs-sm); max-width: var(--measure); }
.verify-body dl { margin: 0; }
.verify-body dl > div { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr); gap: var(--s-2) var(--s-5); padding: var(--s-3) 0; border-top: 1px solid var(--border); }
.verify-body dt { color: var(--text-2); font-size: var(--fs-xs); line-height: var(--lh-meta); font-variant-numeric: tabular-nums; }
.verify-body dd { margin: 0; }
.hash { font-size: var(--fs-xs); line-height: var(--lh-meta); color: var(--text-3); word-break: break-all; font-variant-numeric: tabular-nums; letter-spacing: .02em; }

/* ============================ FOOTER ============================ */
.footer { border-top: 1px solid var(--border); padding: var(--s-5) 0; }
.footer-in { display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5); justify-content: space-between; align-items: baseline; }
/* The site's links are otherwise pills or navigation, so this is the inline
   affordance the wording needs when a claim points at the page that backs it. */
.link { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
.link:hover { color: var(--accent-hover); }
.footer-mark { font-weight: 600; font-size: var(--fs-sm); }
.footer-note { color: var(--text-3); font-size: var(--fs-xs); }
.footer-note .link + .link { margin-left: var(--s-3); }

/* ============================ CONTACT FORM ============================
   A form is a form: labels above their fields, fields in reading order, one
   column of them. Nothing here floats and nothing is a card, for the same
   reason nothing else on the page is.

   Every state the script can put the form in is styled here, and that is not
   tidiness: a state written in the script and nowhere in the stylesheet is a
   state that does not exist, so the harness reads both halves for the same
   five words. `:empty` hides an error with no text, which is what makes the
   markup honest before the script has ever run. */
.contact-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s-8); align-items: start; }
.contact-form-wrap { max-width: 34rem; }
.contact-form { display: grid; gap: var(--s-4); }
.field { display: grid; gap: var(--s-2); }
.field label { color: var(--text-2); font-size: var(--fs-sm); font-weight: 600; }
.field input, .field textarea {
  width: 100%; min-height: var(--control-h-lg); padding: var(--s-3) var(--s-4);
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  font-family: inherit; font-size: var(--fs-md); line-height: var(--lh-body);
  transition: border-color var(--t-state) var(--ease), background-color var(--t-state) var(--ease);
}
.field textarea { resize: vertical; }
.field input:hover, .field textarea:hover { border-color: var(--text-3); }
.field input:focus-visible, .field textarea:focus-visible { outline-offset: 0; border-color: var(--accent); }
.field[data-invalid='true'] input, .field[data-invalid='true'] textarea { border-color: var(--bad); }
.field[data-invalid='true'] label { color: var(--text); }
.field-error { margin: 0; color: var(--bad); font-size: var(--fs-xs); line-height: var(--lh-meta); }
.field-error:empty { display: none; }

.form-action { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4); }
.form-state { margin: 0; max-width: 36ch; color: var(--text-3); font-size: var(--fs-sm); line-height: var(--lh-meta); }
.contact-form[data-state='invalid'] .form-state { color: var(--bad); }
.contact-form[data-state='sending'] .form-state { color: var(--text-2); }
.contact-form[data-state='sending'] .btn { opacity: .55; }
.contact-form[data-state='sent'] .form-state { color: var(--ok); }
.contact-form[data-state='error'] .form-state { color: var(--bad); }
.contact-form[data-state='unconfigured'] .form-state { color: var(--text-2); }
/* Signed out is not an error: nothing has gone wrong, the reader simply is not
   the person this form can be used by yet, so it reads in the quieter level
   rather than in the colour that means a failure. */
.contact-form[data-state='signedout'] .form-state { color: var(--text-2); }
.contact-form[data-state='signedout'] .btn { opacity: .55; }

/* The form host's credit. It is the only link on this page that points off the
   site, so it is set in the smallest type the page has and in the quietest of
   its three text levels - present, legible, and asking for nothing. The inline
   styling this arrived with (11px, #888) would have been a fourth text level at
   a size no token allows, and #888 fails contrast on this surface. */
.splitforms-attribution { margin: var(--s-4) 0 0; color: var(--text-3); font-size: var(--fs-xs); }
.splitforms-attribution a { color: inherit; text-decoration: none; }
.splitforms-attribution a:hover, .splitforms-attribution a:focus-visible { color: var(--text-2); }

/* ============================ STICKY CTA ============================
   On a phone the masthead's button is one line away until the hero has passed,
   and the download section is thousands of pixels further down. So below the
   tablet breakpoint a bar carries the same offer, and it gets out of the way
   again as soon as the download section is on screen, because a second download
   button beside the real one is not help. Above that width it never appears:
   there the masthead button is the one always in reach.

   Shown by opacity and visibility rather than by a transform, so nothing moves
   across the screen, and the reduced-motion rule already turns the fade off. */
.sticky-cta {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
  background: var(--surface); border-top: 1px solid var(--border);
  padding: var(--s-3) 0 calc(var(--s-3) + env(safe-area-inset-bottom, 0px));
  opacity: 0; visibility: hidden;
  transition: opacity var(--t-state) var(--ease), visibility var(--t-state) var(--ease);
}
.sticky-cta[data-shown='true'] { opacity: 1; visibility: visible; }
.sticky-cta-in { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); }
.sticky-cta-text { margin: 0; color: var(--text-2); font-size: var(--fs-xs); line-height: var(--lh-meta); }
.sticky-cta-text b { display: block; color: var(--text); font-size: var(--fs-sm); }

/* ============================ RESPONSIVE ============================
   Fewer columns, never less information - and never less navigation. */
@media (max-width: 900px) {
  .opening, .band, .limits, .download { padding: var(--s-8) 0; }
  .opening-copy { max-width: none; }
  .limits-grid, .download-grid, .contact-grid { grid-template-columns: minmax(0, 1fr); gap: var(--s-6); }
  .contact-form-wrap { max-width: none; }
  .tools { grid-template-columns: minmax(0, 1fr); }

  /* The bar becomes two rows and the navigation becomes a rail that scrolls
     sideways. It used to be hidden outright below this width, which left a
     5,000px page with no wayfinding at all on the screens most likely to read
     it in one hand. Every link stays laid out and every link stays reachable,
     including by keyboard. */
  .masthead-in { min-height: 0; padding-block: var(--s-2); }
  .masthead-nav {
    order: 4; flex-basis: 100%; margin-left: 0; gap: var(--s-1);
    overflow-x: auto; overscroll-behavior-x: contain;
    scrollbar-width: none;
  }
  .masthead-nav::-webkit-scrollbar { display: none; }
  .masthead-nav a { padding: 0 var(--s-3); }
}

@media (max-width: 760px) {
  /* The bar exists only at this width, which is the whole point of it. Its
     control is a finger-sized control for the same reason: this width is a
     phone, and waiting for the pointer to admit it is how a control ends up
     36px tall on the screen it was added for. */
  .sticky-cta { display: block; }
  .sticky-cta .btn { min-height: var(--control-h-lg); }

  .shot-pair { grid-template-columns: minmax(0, 1fr); gap: var(--s-6); }

  /* The table becomes labelled rows rather than a horizontal scroll. The
     labels come from data-label, so nothing is dropped. */
  .catalog thead { display: none; }
  .catalog tbody, .catalog tr, .catalog th, .catalog td { display: block; }
  .catalog tr { padding: var(--s-4) 0; border-bottom: 1px solid var(--border); }
  .catalog th, .catalog td { padding: 0; border: 0; }
  .catalog td { margin-top: var(--s-2); }
  .catalog td::before { content: attr(data-label) "  "; color: var(--text-3); font-size: var(--fs-xs); }
  .catalog td:last-child { font-size: var(--fs-sm); }
  .catalog .num { text-align: left; }
  .sub-head { margin-top: var(--s-7); }
  .option { grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
  .option-action { align-items: flex-start; }
  .option-state { text-align: left; }
  .option .btn { justify-self: start; }
  .tier { grid-template-columns: 8px minmax(0, 1fr) 3rem; row-gap: var(--s-1); }
  .tier-meaning { grid-column: 2 / -1; }
  .verify-body dl > div { grid-template-columns: minmax(0, 1fr); }

  /* The bar is fixed over the end of the page, so the page has to be able to
     scroll clear of it or the last line of the footer sits under it forever.
     The padding is the space the footer already had plus the height of the bar
     it now has to clear, and it is measured rather than guessed: with this at
     var(--s-7) the footer's last line sat 35px underneath the bar. */
  .footer { padding-bottom: calc(var(--s-8) + var(--control-h-lg)); }

  /* Below this width the smallest segment (5 of 104) is narrower than its own
     numeral, so the label moves to the list underneath instead of being
     clipped. The bar still reads in order, and its aria-label still states all
     three numbers. */
  .riskbar-seg { color: transparent; }
}

/* Touch devices get a finger-sized control. A pointer-accurate device keeps
   the compact height, which is what this UI is designed around. */
@media (pointer: coarse) {
  .btn { min-height: var(--control-h-lg); padding: var(--s-3) var(--s-4); }
  .masthead-nav a { min-height: var(--control-h-lg); }
}

/* High-contrast mode overrides colour, so the measured bar and the figures need
   an edge that survives it. */
@media (forced-colors: active) {
  .riskbar-seg { outline: 1px solid CanvasText; outline-offset: -1px; }
  .app-window img, .shot img { border-color: CanvasText; }
  .verify { border-color: CanvasText; }
  /* A border that carries a meaning is drawn as an edge too, because the
     colours that say "this field is wrong" are the first thing high-contrast
     mode replaces. */
  .field[data-invalid='true'] input, .field[data-invalid='true'] textarea,
  .field input, .field textarea { border-color: CanvasText; }
  .sticky-cta { border-color: CanvasText; }
}

/* ============================ MOTION ============================
   There is nothing to animate on load and nothing to animate on scroll. These
   transitions exist to confirm an interaction, and one rule turns every one of
   them off when the reader asks - the universal selector rather than a list,
   because a list is a thing that goes stale when someone adds a component. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; }
  html { scroll-behavior: auto; }
}
