/* FirearmLog website — shared styles. iOS-flavored, light/dark aware.
   July 6 2026 pass: page-local style blocks graduated in here (calc/story/faq),
   Space Grotesk wordmark, brand-link header, skip link, eyebrow utility,
   steps strip, no-JS-safe drill-down, token cleanup. One component system. */

/* Wordmark font (locked brand: Space Grotesk 700, wordmark only — body stays system).
   Self-hosted (OFL license), latin subset, ~13KB. */
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("./fonts/SpaceGrotesk-Bold-latin.woff2") format("woff2");
}

:root {
  /* color tokens */
  --bg: #f2f2f7;
  --bg-elev: #ffffff;
  --card: #ffffff;
  --text: #1c1c1e;
  --text-dim: #6c6c70;
  /* Brand amber — mirrors the app's locked tokens. Amber-as-a-fill is only ~2.4:1
     on white, so TEXT/links/focus rings use --accent-ink (the darker amber), and
     buttons put DARK ink on the amber fill. FILLS use --accent. */
  --accent: #E8820E;          /* fill: buttons, tints, borders, chart-ish accents */
  --accent-ink: #9A5200;      /* text/links/active-nav/focus ring — 5.9:1 on white (AA) */
  --accent-text: #2a1800;     /* dark ink for text sitting ON an amber fill (button labels) */
  --btn-bg: #E8820E;          /* amber button fill (dark ink on it, not white) */
  --btn-bg-hover: #C46F00;
  --danger: #c20015; /* WCAG-AA red on light card, for signup errors */
  --border: #d8d8dc;
  --border-strong: #767676;  /* >=3:1 control boundary (WCAG 1.4.11), light mode */
  --hairline: #e5e5ea;
  --tag-bg: #eaeaef;
  /* The scroll cue on a wide table. Dark ink on a light surface. */
  --cue-shadow: rgba(0,0,0,0.30);

  /* radii */
  --radius: 16px;
  --radius-lg: 24px;
  --radius-pill: 999px;
  --radius-sm: 12px;
  --radius-xs: 10px;
  --radius-xxs: 8px;

  /* layout */
  --maxw: 1040px;

  /* fluid type scale (bumped up a few points — June 2026, Michael's call) */
  --fs-hero: clamp(2.6rem, 6.4vw, 4.3rem);
  --fs-tagline: clamp(1.3rem, 2.7vw, 1.72rem);
  --fs-h2: clamp(1.7rem, 3.4vw, 2.4rem);
  --fs-h3: 1.3rem;
  --fs-lede: clamp(1.18rem, 1.9vw, 1.45rem);
  --fs-body: 1.1rem;
  --fs-ui: 1.05rem;
  --fs-small: 0.98rem;
  --fs-xs: 0.8rem;

  /* spacing scale */
  --sp-1: 0.5rem;
  --sp-2: 1rem;
  --sp-3: 1.5rem;
  --sp-4: 2rem;
  --sp-5: 3rem;
  --sp-6: 4rem;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-brand: "Space Grotesk", var(--font);
  --shadow: 0 10px 40px rgba(0,0,0,0.10);
  --shadow-soft: 0 4px 18px rgba(0,0,0,0.07);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #000000;
    --bg-elev: #121214;
    --card: #1c1c1e;
    --text: #f5f5f7;
    --text-dim: #a1a1a6;
    --accent: #FF9F0A;          /* brighter amber fill on dark */
    --accent-ink: #FFB340;      /* amber text/links on dark — high contrast on dark cards */
    --btn-bg: #FF9F0A;          /* dark ink (--accent-text #2a1800) still reads on bright amber */
    --btn-bg-hover: #FFB340;
    --danger: #ff6961; /* brighter red for contrast on dark card */
    --border: #38383a;
    --border-strong: #6e6e73;  /* >=3:1 control boundary, dark mode */
    --hairline: #2a2a2c;
    --tag-bg: #2a2a2c;
    /* Inverted: a black shadow is invisible against a dark card, so the cue is light. */
    --cue-shadow: rgba(255,255,255,0.34);
    --shadow: 0 10px 40px rgba(0,0,0,0.5);
    --shadow-soft: 0 4px 18px rgba(0,0,0,0.4);
  }
}

/* PRINT — on paper, colour is not a preference, it is ink.
   Browsers print with backgrounds OFF by default. That drops a dark scheme's
   background but NOT its light text colour, so a visitor whose device prefers dark
   printed #f5f5f7 onto white paper: 1.09:1 measured, against a 4.5:1 floor — a
   sheet that looks blank. Forcing backgrounds on is worse; the page comes out as a
   bled black rectangle. The match-day checklist is the one page whose entire job is
   being printed and carried to a match, so this is a real failure, not a nicety.
   Both media queries can match at once, so this block sits AFTER the dark-scheme
   block deliberately: equal specificity, later in source, and it wins. */
@media print {
  :root {
    --bg: #ffffff;
    --bg-elev: #ffffff;
    --card: #ffffff;
    --text: #000000;
    --text-dim: #333333;
    --accent: #000000;
    --accent-ink: #000000;
    --border: #999999;
    --border-strong: #666666;
    --hairline: #cccccc;
    --tag-bg: #ffffff;
    /* Paper cannot scroll; the cue must not spend ink. */
    --cue-shadow: transparent;
    --shadow: none;
    --shadow-soft: none;
    /* The button fills were the one colour pair this block never reset, so .btn-primary
       printed its full screen amber (measured rgb(232,130,14)) on every page carrying a
       call to action - ink spent on a control nobody can press on paper. */
    --btn-bg: #ffffff;
    --btn-bg-hover: #ffffff;
  }
  body { background: #ffffff; color: #000000; }
  /* A printed link cannot be followed, so mark it by underline rather than by a
     colour that may not survive a monochrome printer. */
  a { color: #000000; text-decoration: underline; }
  /* A white fill on white paper would vanish, so the printed button keeps its outline. */
  .btn-primary { color: #000000; border: 1px solid #999999; }
  /* The site chrome is not part of the document a shooter carries to a match. A sticky
     header - including an "Open the web app" button that cannot be pressed on paper - and
     the entire footer link row printed at the top and bottom of every sheet. index.html
     and match-day-checklist.html each fixed this inside their own private block; the
     SHARED sheet never did, so it was still true on the other 16 pages. Same shape as the
     round-10 checklist fix, applied once and never generalised. */
  header.site, footer.site, .skip-link, .nav-scrim { display: none !important; }
}

* { box-sizing: border-box; }
/* scroll-padding-top clears the position:sticky header (measured 61px on every page at
   every width, 49 combinations) so an anchor jump never parks its target underneath it.
   Set on html rather than on the individual headings so it covers every anchor on the
   site, present and future, plus scrollIntoView() and focus scrolling. Guides > Understand
   your scores and Guides > New to competition both landed with the heading fully hidden. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: 76px; }
body {
  margin: 0;
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  line-height: 1.55;
  font-size: var(--fs-body);
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent-ink); text-decoration: none; }
a:hover { text-decoration: underline; }
/* Links inside running text must not be identified by colour alone. Against the
   surrounding body text --accent-ink measures 2.90:1 in light mode and 1.64:1 in
   dark, both under the 3:1 that WCAG 1.4.1 requires before colour on its own may
   mark a link, so prose links carry a permanent underline. Navigation, buttons,
   CTAs and cards all carry a class and are excluded; footer links sit outside
   p/li and are unaffected. */
/* `td` joined this list on 29 July 2026, when the eight-stages table became the first
   table on the site to carry a link in a cell. The print check (section I) already
   asserted `td > a`, so the standard was written down before anything satisfied it: a
   link in a table cell is running text and must not be identified by colour alone. */
p a:not([class]), li a:not([class]), dd a:not([class]), td a:not([class]), a.inline {
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 0.07em;
}
img { max-width: 100%; display: block; }
h1, h2, h3 { letter-spacing: -0.02em; line-height: 1.1; }

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 20px; }

/* focus visibility (accessibility) */
:focus-visible {
  outline: 3px solid var(--accent-ink);
  outline-offset: 2px;
  border-radius: var(--radius-xxs);
}

/* Skip link: hidden until keyboard focus, then a visible pill under the header */
.skip-link {
  position: absolute; left: -9999px; top: 8px; z-index: 40;
  background: var(--card); color: var(--text); font-weight: 600;
  padding: 10px 16px; border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong); box-shadow: var(--shadow-soft);
}
.skip-link:focus-visible { left: 12px; }

/* ---------- Header ---------- */
header.site {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid var(--hairline);
}
header.site .bar {
  display: flex; align-items: center; gap: 12px;
  height: 60px; max-width: var(--maxw); margin: 0 auto; padding: 0 20px;
}
/* Brand = a real home link (logo + wordmark), universal convention */
header.site .brand { display: inline-flex; align-items: center; gap: 12px; color: var(--text); padding: 8px 0; min-height: 44px; }
header.site .brand:hover { text-decoration: none; }
header.site .logo { width: 30px; height: 30px; border-radius: var(--radius-xxs); }
header.site .name { font-family: var(--font-brand); font-weight: 700; font-size: var(--fs-ui); letter-spacing: -0.02em; }
header.site nav { margin-left: auto; display: flex; gap: 20px; font-size: var(--fs-small); }
header.site nav a { color: var(--text); padding: 12px 2px; }  /* padding buys a >=44px touch target on tablet widths without changing the look */

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-weight: 600; font-size: var(--fs-ui); line-height: 1;
  padding: 15px 26px; border-radius: var(--radius-pill);
  border: 1px solid transparent; cursor: pointer; transition: transform .06s ease, background .2s ease;
  min-height: 52px;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--btn-bg); color: var(--accent-text); }
.btn-primary:hover { background: var(--btn-bg-hover); color: var(--accent-text); }
.btn-secondary { background: var(--card); color: var(--text); border-color: var(--border-strong); }
.btn-block { width: 100%; }

/* ---------- Hero ---------- */
.hero {
  display: grid; grid-template-columns: 1.05fr 0.95fr; gap: var(--sp-6);
  align-items: center; padding: clamp(2.5rem, 6vw, 5.5rem) 0 var(--sp-6);
}
.hero .kicker {
  display: inline-block; font-size: var(--fs-small); font-weight: 600;
  color: var(--accent-ink); letter-spacing: 0.02em; margin-bottom: var(--sp-2);
}
.hero h1 { font-size: var(--fs-hero); margin: 0 0 var(--sp-2); }
.hero .tagline-2 {
  font-size: var(--fs-tagline); font-weight: 600; color: var(--text);
  margin: 0 0 var(--sp-3); letter-spacing: -0.01em;
}
.hero .lede { font-size: var(--fs-lede); color: var(--text-dim); max-width: 36ch; margin: 0 0 var(--sp-3); }

/* Privacy tribal line under the lede — the sharpest differentiator, said early and plainly */
.hero-privacy {
  font-size: var(--fs-small); font-weight: 600; color: var(--text);
  margin: 0 0 0.6rem; letter-spacing: 0.01em;
}
.hero-privacy .sep { color: var(--text-dim); font-weight: 700; padding: 0 6px; }
.hero-sport { font-size: var(--fs-small); color: var(--text-dim); font-style: italic; margin: 0 0 var(--sp-4); }
.cta-note { font-size: var(--fs-xs); color: var(--text-dim); margin: 10px 0 0; }

.cta-row { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }
.coming-soon {
  margin-top: var(--sp-3); font-size: var(--fs-small); color: var(--text-dim);
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.coming-soon .pill {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--tag-bg); color: var(--text-dim);
  border-radius: var(--radius-pill); padding: 5px 12px; font-weight: 600;
}

/* ---------- Email capture ---------- */
.signup { margin-top: var(--sp-4); max-width: 460px; }
.signup .label { font-size: var(--fs-small); font-weight: 600; margin-bottom: 8px; }
.signup form { display: flex; gap: 8px; flex-wrap: wrap; }
.signup input[type="email"] {
  flex: 1 1 200px; min-height: 50px; padding: 0 16px; font-size: 16px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  background: var(--card); color: var(--text);
}
.signup .btn { min-height: 50px; padding: 13px 22px; font-size: 1rem; }
.signup .fine { margin-top: 8px; font-size: var(--fs-xs); color: var(--text-dim); }
.signup-done { margin-top: 10px; font-weight: 600; color: var(--accent-ink); font-size: var(--fs-small); }
.signup-done[hidden] { display: none; }
.signup-error { margin-top: 10px; font-weight: 600; color: var(--danger); font-size: var(--fs-small); }
.signup-error[hidden] { display: none; }
/* aria-disabled, not [disabled] — the busy state has to stay focusable so a keyboard
   user does not lose their place mid-submit (see site.js's setBusy). Both selectors
   kept so the rule survives if any surface still uses the attribute. */
.signup .btn[disabled],
.signup .btn[aria-disabled="true"],
.fl-correction .btn[aria-disabled="true"] { opacity: 0.6; cursor: default; }
/* Quiet variant for the foot of content pages (guides, story) */
.signup-foot { margin-top: var(--sp-5); padding-top: var(--sp-3); border-top: 1px solid var(--hairline); }
/* Honeypot: off-screen, untabbable, hidden from assistive tech. Real users never see or fill it. */
.hp-field {
  position: absolute !important; left: -9999px !important;
  width: 1px; height: 1px; opacity: 0; pointer-events: none;
}

/* ---------- Correction box ("Spot a mistake?") ----------
   Shares the .signup shell, but it is a different job: people type a sentence
   or two here, not an address. An unstyled textarea renders at roughly 180px
   wide and two lines tall, which silently tells the reader "a few words will
   do" and suppresses the detail that makes a correction actionable. Measured,
   not eyeballed (rule 22c). */
.correction-box { max-width: 620px; }
.correction-box form { gap: 10px; }
.correction-box textarea[name="message"] {
  flex: 1 1 100%; width: 100%; box-sizing: border-box;
  min-height: 150px; padding: 12px 16px;
  font-family: inherit; font-size: 16px; line-height: 1.45;
  border: 1px solid var(--border-strong); border-radius: 14px;
  background: var(--card); color: var(--text);
  resize: vertical;
}
/* Theme-aware placeholder. The email inputs previously fell back to the browser default
   grey, which is the same absolute colour in both schemes and measured 3.69:1 on the dark
   background, under the 4.5:1 floor. */
.correction-box textarea[name="message"]::placeholder,
input[type="email"]::placeholder,
input[type="number"]::placeholder { color: var(--text-dim); }
.correction-box label[for="corr-email"] {
  flex: 1 1 100%; margin: 4px 0 -4px;
  font-size: var(--fs-xs); color: var(--text-dim);
}
.correction-box .correction-sig { margin-top: 10px; }

/* ---------- Device mockup (hero + drill-down) ---------- */
/* Bezel radii are bespoke to the mockup component, not the site radius scale. */
.device {
  width: min(280px, 72%); margin: 0 auto;
  background: #000; border-radius: 20px; padding: 5px;
  box-shadow: var(--shadow);
}
.device .screen {
  border-radius: 15px; overflow: hidden; aspect-ratio: 9 / 19.5;
  background: var(--bg-elev); display: flex; align-items: center; justify-content: center;
}
.device .screen img { width: 100%; height: 100%; object-fit: cover; }

/* ---------- Sections ---------- */
section.block { padding: var(--sp-6) 0; }
.section-head { max-width: 56ch; margin: 0 0 var(--sp-4); }
.section-head h2 { font-size: var(--fs-h2); margin: 0 0 var(--sp-2); }
.section-head p { font-size: var(--fs-lede); color: var(--text-dim); margin: 0; }

/* Eyebrow utility — the one small-caps lead-in treatment, shared by story + FAQ sections */
.eyebrow {
  font-size: var(--fs-small); font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--accent-ink); margin: 0 0 0.4rem;
}

/* Founder (homepage) */
.founder { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: var(--sp-5); align-items: start; }
.founder .portrait { width: 100%; aspect-ratio: 4/5; border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-soft); }
.founder .portrait img { width: 100%; height: 100%; object-fit: cover; }
.founder h2 { font-size: var(--fs-h2); margin: 0 0 var(--sp-2); }
.founder p { font-size: var(--fs-lede); color: var(--text); margin: 0 0 var(--sp-2); }

/* Feature grid */
.features { display: flex; flex-wrap: wrap; justify-content: center; gap: 16px; }
.feature { flex: 0 1 300px; background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius); padding: 24px; box-shadow: var(--shadow-soft); }
.feature h3 { margin: 0 0 8px; font-size: var(--fs-h3); }
.feature p { margin: 0; color: var(--text-dim); }

/* "Your first three minutes" strip (activation path, lifted from the FAQ's own script) */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin: var(--sp-4) 0 0; }
.step { background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius); padding: 20px 22px; box-shadow: var(--shadow-soft); text-align: left; }
.step .num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--btn-bg); color: var(--accent-text);
  font-weight: 700; font-size: var(--fs-small); margin-bottom: 10px;
}
.step h3 { margin: 0 0 6px; font-size: var(--fs-ui); }
.step p { margin: 0; color: var(--text-dim); font-size: var(--fs-small); }
.steps-note { margin: var(--sp-3) auto 0; font-size: var(--fs-small); color: var(--text-dim); max-width: 52ch; }

/* Callout / final CTA */
.callout {
  background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius-lg);
  padding: clamp(28px, 5vw, 52px); text-align: center; box-shadow: var(--shadow-soft);
}
.callout h2 { font-size: var(--fs-h2); margin: 0 0 var(--sp-2); }
.callout p { color: var(--text-dim); max-width: 52ch; margin: 0 auto var(--sp-3); font-size: var(--fs-lede); }
.callout .cta-row { justify-content: center; }
.callout .signup { margin-left: auto; margin-right: auto; }

/* Article (guides / privacy / support) */
article { background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius); padding: clamp(24px, 4vw, 40px); margin: var(--sp-5) 0; box-shadow: var(--shadow-soft); }
article h1 { font-size: var(--fs-h2); margin-top: 0; }
article h2 { font-size: 1.45rem; margin-top: var(--sp-4); } /* was 1.25rem — a 13% step over body read as flat hierarchy */
article h3 { font-size: 1.2rem; margin-top: var(--sp-3); } /* was 1.08rem — smaller than body text (17.28 vs 17.6px) */
/* A heading that is ENTIRELY a link is the primary target of the page it indexes — the
   guides index is ten of them, and they are how a shooter gets to any guide at all. Left
   inline they measured 21px tall at 1440 and 42px only where a title happened to wrap onto
   two lines, well under the 44px floor this site holds everywhere else. Padding rather than
   a fixed height, so a two-line title grows instead of clipping. */
article h3 > a { display: inline-block; padding-block: 12px; margin-block: -6px; }
article p, article li { color: var(--text); }
article .updated { color: var(--text-dim); font-size: var(--fs-small); }
article ul { padding-left: 22px; }
article li { margin: 6px 0; }
article p, article li { max-width: 70ch; }

/* ---------- My Story (graduated from the page style block, on tokens) ---------- */
.story { max-width: 720px; }
.story h1 { font-size: var(--fs-h2); line-height: 1.08; margin: 0 0 1.4rem; }
.story .lede-1 { font-size: var(--fs-lede); line-height: 1.5; font-weight: 500; }
.story p { font-size: var(--fs-body); line-height: 1.7; margin: 0 0 1.15rem; }
.story h2 { font-size: 1.5rem; line-height: 1.2; margin: 2.4rem 0 0.9rem; }
.story .pullquote { margin: 2.2rem 0; padding: 0; border: 0; text-align: center; }
.story .pullquote p { font-size: 1.9rem; line-height: 1.25; font-weight: 600;
  font-style: italic; margin: 0; letter-spacing: -0.01em; }
.story .portrait { width: 100%; height: auto; border-radius: var(--radius); margin: 1.6rem 0 0.6rem; display: block; }
.story .portrait-cap { font-size: var(--fs-small); color: var(--text-dim); text-align: center; margin: 0 0 1.6rem; }
.story .endmark { margin-top: 2.6rem; padding-top: 1.8rem; border-top: 1px solid var(--hairline); }
.story .cta { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 1.4rem; }
@media (max-width: 700px) {
  .story .pullquote p { font-size: 1.45rem; }
}

/* ---------- FAQ (graduated from the page style block, on tokens) ---------- */
.faq-lede {
  margin: var(--sp-3) 0 var(--sp-5);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius);
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 8%, var(--card)), var(--card));
  border: 1px solid var(--hairline);
}
.faq-lede h2 { margin-top: 0; }
.faq-lede p { font-size: var(--fs-lede); line-height: 1.5; margin-bottom: 0; }

.faq section { margin-top: var(--sp-5); }
.faq details.faq-item { border-bottom: 1px solid var(--hairline); }
.faq section > details.faq-item:first-of-type { border-top: 1px solid var(--hairline); }
.faq summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 15px 2px; min-height: 24px;
  font-weight: 600; font-size: var(--fs-ui); line-height: 1.35; color: var(--text);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; flex: none; width: 9px; height: 9px; margin-right: 5px;
  border-right: 2px solid var(--text-dim); border-bottom: 2px solid var(--text-dim);
  transform: rotate(45deg); transition: transform 0.2s ease;
}
.faq details[open] > summary::after { transform: rotate(-135deg); }
.faq summary:hover { color: var(--accent-ink); }
.faq .faq-answer { padding: 0 2px 16px; }
.faq .faq-answer p { margin-top: 0; }
.faq .faq-answer p + p { margin-top: 10px; }

.faq-boundaries {
  margin-top: var(--sp-5); padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius); background: var(--card);
  border: 1px solid var(--border); border-left: 4px solid var(--accent);
}
.faq-boundaries h2 { margin-top: 0; }

/* ---------- Calculators (graduated from the tools style block, on tokens) ---------- */
.calc { background: var(--card); border: 1px solid var(--hairline); border-radius: var(--radius); padding: 1.4rem 1.5rem; margin-bottom: var(--sp-3); box-shadow: var(--shadow-soft); }
.calc h2 { font-size: var(--fs-h3); margin: 0 0 4px; }
.calc .note { font-size: var(--fs-small); color: var(--text-dim); margin: 0 0 16px; }
.flds { display: grid; gap: 14px; }
.fld { display: flex; flex-direction: column; }
.fld label { display: block; font-size: var(--fs-small); color: var(--text-dim); margin-bottom: 5px; }
/* No native number-spinners: they clutter the field and add sub-44px micro-targets
   (same lesson as the app's stepper work) — our fields are type-and-go. */
.fld input[type="number"]::-webkit-outer-spin-button, .fld input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.fld input[type="number"] { -moz-appearance: textfield; }
.fld input, .fld select { margin-top: auto; width: 100%; min-height: 46px; padding: 0 12px; font-size: 16px; border: 1px solid var(--border-strong); border-radius: var(--radius-xs); background: var(--bg-elev); color: var(--text); font-family: var(--font); }
.result { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 12px; background: var(--bg); border-radius: var(--radius-sm); padding: 14px 16px; margin-top: 16px; }
.result .lab { font-size: var(--fs-small); color: var(--text-dim); }
.result .big { font-size: 1.7rem; font-weight: 700; color: var(--text); }
.result .big .amber { color: var(--accent-ink); }
.gap { overflow-wrap: anywhere; font-size: var(--fs-small); color: var(--text-dim); margin: 8px 0 0; }
/* ---- The classification fork ------------------------------------------------------
   USPSA's published algorithm states the window before a first classification two ways.
   Where the two readings put the same shooter in different classes, the page shows both
   rather than hedging: a flag beside the result that expands IN PLACE, so the number and
   the explanation stay in one eye-span. Hidden whenever the readings agree. Lives in the
   shared sheet, not in a page-private block — private copies are where this site's
   defects breed. */
.fork { margin-top: 12px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg); }
.fork > summary { cursor: pointer; list-style: none; padding: 11px 14px; min-height: 44px; box-sizing: border-box; display: flex; align-items: center; gap: 8px; font-size: var(--fs-small); font-weight: 600; color: var(--accent-ink); }
.fork > summary::-webkit-details-marker { display: none; }
.fork > summary::after { content: ''; margin-left: auto; flex: none; width: 8px; height: 8px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); transition: transform .15s ease; }
.fork[open] > summary::after { transform: rotate(-135deg); }
.fork .fork-flag { flex: none; }
.fork-body { padding: 0 14px 14px; font-size: var(--fs-small); color: var(--text); }
.fork-body p { margin: 0 0 10px; }
.fork-body p:last-child { margin-bottom: 0; }
.fork-body blockquote { margin: 0 0 10px; padding: 6px 0 6px 14px; border-left: 3px solid var(--accent); color: var(--text-dim); font-style: italic; }
.fork-body .fork-cite { color: var(--text-dim); }
.fork-body .fork-cite a { color: var(--accent-ink); font-weight: 600; }
.invite { font-size: var(--fs-small); color: var(--text-dim); margin: 10px 0 0; }
.invite a { color: var(--accent-ink); font-weight: 600; }
.g4 { grid-template-columns: repeat(4, 1fr); }
.g2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 560px) { .g4 { grid-template-columns: repeat(2, 1fr); } }
/* Copy-a-link-to-this-result (shareable calculators) */
.copylink {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 10px; padding: 10px 16px; min-height: 44px;
  font-size: var(--fs-small); font-weight: 600; font-family: var(--font);
  color: var(--accent-ink); background: none;
  border: 1px solid var(--border-strong); border-radius: var(--radius-pill); cursor: pointer;
}
.copylink:hover { background: var(--tag-bg); }
/* Author-origin display beats the browser's [hidden]{display:none} (same cascade quirk
   already fixed for .af-row) — without this, the share button shows on empty calculators. */
.copylink[hidden] { display: none !important; }

/* Footer */
footer.site { border-top: 1px solid var(--hairline); padding: var(--sp-5) 0; color: var(--text-dim); font-size: var(--fs-small); margin-top: var(--sp-5); }
/* The footer guide index. Ten guides, on all 22 pages, so every guide carries a sitewide
   internal link without an eleven-item submenu in the primary nav (see the nav note in
   the session-92 commit). Tokens here are the SHARED sheet's; index.html carries its own
   vocabulary and its own copy of these rules, and section R checks the two stay in step. */
footer.site .foot-guides { display: flex; flex-wrap: wrap; gap: 20px 56px; padding: 0 0 var(--sp-4); }
footer.site .foot-guides-group { min-width: 230px; }
footer.site .foot-guides-head { font-size: var(--fs-small); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text); margin: 0 0 4px; }
footer.site .foot-guides ul { list-style: none; margin: 0; padding: 0; }
footer.site .foot-guides li { margin: 0; }
footer.site .foot-guides a { display: block; min-width: 0; padding: 12px 0; }  /* 9px measured 38px tall, six under the 44px floor */
footer.site .row { display: flex; gap: 18px; flex-wrap: wrap; align-items: center; }
footer.site .row .spacer { margin-left: auto; }
footer.site a { color: var(--text-dim); display: inline-flex; align-items: center; min-width: 44px; padding: 12px 4px; }  /* short labels ("FAQ") measured 31px wide */

/* ---------- Responsive ---------- */
@media (max-width: 860px) {
  .hero { grid-template-columns: 1fr; gap: var(--sp-4); text-align: center; padding-top: var(--sp-5); }
  .hero .lede { margin-left: auto; margin-right: auto; }
  .cta-row, .coming-soon { justify-content: center; }
  .signup { margin-left: auto; margin-right: auto; }
  .hero .device { grid-row: 1; margin-bottom: var(--sp-2); }
  .founder { grid-template-columns: 1fr; }
  .founder .portrait { max-width: 320px; margin: 0 auto var(--sp-3); }
  .founder-text { text-align: left; }
  .steps { grid-template-columns: 1fr; max-width: 420px; margin-left: auto; margin-right: auto; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; }
}

/* ---------- Drill-down (pinned, scroll-scrubbed navigation, Apple-style) ----------
   Each .depth-drill is a TALL block; its .depth-pin sticks for the duration while the
   JS advances the screen + converging mask by scroll progress. --steps (set inline)
   drives the block height so each step gets a comfortable slice of scroll.
   NO-JS SAFE: the tall height + cue only exist when JS adds .js to <html>;
   without JS each drill is a normal-height block showing its final detail screen. */
.depth-drill { position: relative; height: auto; }
.depth-drill .depth-pin { position: static; height: auto; padding: var(--sp-4) 0; display: flex; align-items: center; }
html.js .depth-drill { height: calc(var(--steps, 6) * 50vh + 100vh); }
html.js .depth-pin { position: sticky; top: 60px; height: calc(100vh - 60px); height: calc(100dvh - 60px); padding: 0; }
.depth-grid { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: var(--sp-5); align-items: center; width: 100%; }
.depth-stage { display: flex; justify-content: center; }
.depth-stage .device .screen { position: relative; }
.d-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .7s ease; z-index: 1; }
.d-img.active { opacity: 1; }
.d-spot { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.72); opacity: 0; pointer-events: none; z-index: 2;
  transition: opacity .6s ease, clip-path 1.1s cubic-bezier(.33, 0, .2, 1);
  clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%, 0% 0%, 1% 1%, 1% 99%, 99% 99%, 99% 1%, 1% 1%); }
.d-spot.lit { opacity: 1; }

/* Cursor that dissolves/drifts to each click point just before the mask converges. */
.d-cursor { position: absolute; left: 50%; top: 50%; width: 26px; height: 26px; margin: -4px 0 0 -4px; z-index: 3; opacity: 0; pointer-events: none;
  transition: left .6s cubic-bezier(.33, 0, .2, 1), top .6s cubic-bezier(.33, 0, .2, 1), opacity .5s ease; }
.d-cursor.show { opacity: 1; }
.d-cursor svg { display: block; position: relative; z-index: 2; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .45)); }
.d-cursor .d-ring { position: absolute; left: -7px; top: -7px; width: 34px; height: 34px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, .35); opacity: 0; z-index: 1; }
.d-cursor.show .d-ring { animation: tapPulse 1.7s ease-out infinite; }
@keyframes tapPulse { 0% { transform: scale(.35); opacity: .65; } 70% { opacity: 0; } 100% { transform: scale(1.5); opacity: 0; } }

/* "Scroll to drive it" cue at the first drill; only exists with JS, fades once the user scrolls. */
.scroll-cue { display: none; text-align: center; color: var(--text-dim); margin: calc(-1 * var(--sp-3)) 0 var(--sp-4); font-size: var(--fs-small); transition: opacity .4s ease; }
html.js .scroll-cue { display: block; }
.scroll-cue.hide { opacity: 0; }
.scroll-cue .cue-text { display: block; letter-spacing: .02em; }
.scroll-cue .chev { display: inline-block; font-size: 1.6rem; line-height: 1; margin-top: 2px; color: var(--accent-ink); animation: cueBob 1.6s ease-in-out infinite; }
@keyframes cueBob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) { .d-cursor .d-ring, .scroll-cue .chev { animation: none; } }
.depth-copy h3 { font-size: var(--fs-h2); margin: 0 0 var(--sp-2); line-height: 1.12; }
.depth-copy p { color: var(--text-dim); max-width: 40ch; font-size: var(--fs-ui); }
@media (max-width: 900px) {
  /* Phones: no tall blocks, no pin/scrub — each drill just shows its final detail, stacked. */
  html.js .depth-drill { height: auto; }
  html.js .depth-pin { position: static; height: auto; padding: var(--sp-5) 0; }
  .depth-grid { grid-template-columns: 1fr; gap: var(--sp-3); justify-items: center; }
  .depth-stage .device { width: 200px; }
  .depth-copy { text-align: center; }
  .depth-copy p { margin: 0 auto; }
  html.js .scroll-cue { display: none; }
}

/* Quiet founder hook in the hero (surfaces the story early; links to the full block / My Story page) */
.hero-byline { font-size: var(--fs-small); color: var(--text-dim); margin: 0.4rem 0 0; }
.hero-byline a { color: var(--accent-ink); text-decoration: none; font-weight: 600; white-space: nowrap; }
.hero-byline a:hover { text-decoration: underline; }

/* Stakes / "why it matters" section (board: the missing arc, session 26) */
.stakes .stakes-head { max-width: 60ch; }
.stakes-head .stakes-sub { display: block; margin-top: 8px; font-size: var(--fs-lede); font-weight: 600; color: var(--text-dim); }
.stakes-body { max-width: 56ch; }
.stakes-body p { font-size: var(--fs-lede); color: var(--text); margin: 0 0 var(--sp-3); }
.stakes-body p:last-child { margin-bottom: 0; }
.stakes-body strong { color: var(--text); }

/* Mobile nav -- COMPACT FROSTED DROPDOWN (June 29 2026 redesign #2).
   The menu drops straight down from the hamburger, sized to just the links
   (left-justified). The rest of the page stays in place and fully legible --
   only the panel itself is the translucent, blurred, darkened (frosted-glass) area. */
.nav-toggle { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Desktop: the checkbox has no visible UI, so keep it OUT of the tab order entirely (WCAG 2.4.7) */
@media (min-width: 701px) { .nav-toggle { display: none; } }
.nav-burger { display: none; }
.nav-scrim { display: none; }
@media (max-width: 700px) {
  header.site .bar { height: 60px; }  /* fixed height -> opening the menu never pushes the page down */
  /* Solid header on mobile so the dropdown's frosted blur can render (a blurred panel won't blur
     while nested inside an already-backdrop-filtered header). */
  header.site { background: var(--bg); -webkit-backdrop-filter: none; backdrop-filter: none; }
  /* flex-shrink:0 — at a 320px viewport the brand + CTA + burger overflowed .bar and the
     burger was compressed to 24px with 0px-wide icon bars: an unmarked blank box where the
     menu should be. */
  /* Very narrow phones (iPhone SE, 320px): brand 131px + CTA 111px + burger 46px + gaps
     overflowed .bar by 4px, which is a WCAG 1.4.10 reflow failure at the mandated test
     width. Tighten the gutter and the CTA's padding rather than hiding a control. */
  @media (max-width: 360px) {
    header.site .bar { gap: 6px; padding: 0 10px; }
    header.site .btn-app { padding: 9px 10px; font-size: 0.85rem; }
    header.site .brand { min-width: 0; }
    header.site .name { font-size: 0.98rem; }
  }
  .nav-burger { display: inline-flex; flex-shrink: 0; flex-direction: column; justify-content: center; gap: 5px; width: 46px; height: 44px; margin-left: auto; padding: 0 11px; border: 0.5px solid var(--border); border-radius: var(--radius-xs); background: var(--bg-elev); cursor: pointer; position: relative; z-index: 32; }
  .nav-burger span { display: block; height: 2px; background: var(--text); border-radius: 2px; transition: transform .2s ease, opacity .2s ease; }

  /* Compact frosted panel: only as wide/tall as the links; anchored under the hamburger (top-right). */
  header.site nav {
    position: fixed; top: 58px; right: 12px; left: auto; z-index: 31;
    display: flex; flex-direction: column; gap: 0;
    width: max-content; min-width: 150px; max-width: 72vw;
    text-align: left;
    background: rgba(28,28,30,0.72);
    -webkit-backdrop-filter: saturate(170%) blur(20px);
    backdrop-filter: saturate(170%) blur(20px);
    border: 0.5px solid rgba(255,255,255,0.14);
    border-radius: 14px;
    box-shadow: 0 18px 44px rgba(0,0,0,0.40);
    padding: 6px;
    transform-origin: top right;
    transform: translateY(-8px) scale(0.97); opacity: 0; visibility: hidden; pointer-events: none;
    transition: transform .2s ease, opacity .2s ease, visibility .2s;
  }
  header.site nav a {
    color: #fff; font-size: 1.02rem; font-weight: 500;
    padding: 11px 14px; min-height: 44px;
    display: flex; align-items: center; justify-content: flex-start;
    border: 0; border-radius: var(--radius-xxs); white-space: nowrap;
  }
  header.site nav a:hover, header.site nav a:active { background: rgba(255,255,255,0.14); text-decoration: none; }

  /* Invisible tap-catcher: tapping anywhere off the menu closes it. Transparent, so the
     page stays wholly legible (no whole-page blur or dim -- only the panel is frosted). */
  .nav-scrim {
    display: block; position: fixed; inset: 0; z-index: 30;
    background: transparent;
    opacity: 0; visibility: hidden; pointer-events: none;
  }

  /* OPEN state */
  .nav-toggle:checked ~ nav { transform: translateY(0) scale(1); opacity: 1; visibility: visible; pointer-events: auto; }
  .nav-toggle:checked ~ .nav-scrim { opacity: 1; visibility: visible; pointer-events: auto; }
  body:has(.nav-toggle:checked) { overflow: hidden; }

  /* burger -> X */
  .nav-toggle:checked ~ .nav-burger span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav-toggle:checked ~ .nav-burger span:nth-child(2) { opacity: 0; }
  .nav-toggle:checked ~ .nav-burger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
  .nav-toggle:focus-visible ~ .nav-burger,
  .nav-burger:focus-visible { box-shadow: 0 0 0 3px var(--accent-ink); outline: none; }
  /* Solid, not 45% alpha: composited against the header the ring measured 2.02:1, under the
     3:1 floor WCAG 1.4.11 sets for a focus indicator. This is the outline that tells a
     keyboard user where they are. */
}
@media (prefers-reduced-motion: reduce) {
  header.site nav, .nav-scrim { transition: none; }
}

/* ---- Tools submenu: one markup, two presentations (session 90) ----
   Desktop keeps the nav to one row, so Tools is a disclosure that drops the calculators
   below it. The phone panel is already a vertical list, so there is nothing to disclose:
   the calculators simply sit indented under Tools and no new interaction has to be learned.
   Built on the same visually-hidden-checkbox mechanism the burger already uses, so it works
   with JavaScript switched off; site.js enhances the label into a real button on top of it. */
.nav-tools { position: relative; display: inline-flex; align-items: center; }
.nav-tools-toggle { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.nav-tools-link { display: none; }  /* the phone's plain Tools link */
.nav-tools-label { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; color: var(--text); padding: 12px 2px; min-height: 44px; }
.nav-caret { font-size: 0.7em; line-height: 1; transition: transform .16s ease; }
.nav-tools-toggle:checked ~ .nav-tools-label .nav-caret { transform: rotate(180deg); }
/* Solid 3px, matching the burger's ring for the same WCAG 1.4.11 reason. */
.nav-tools-toggle:focus-visible ~ .nav-tools-label,
.nav-tools-label:focus-visible { outline: 3px solid var(--accent-ink); outline-offset: 2px; border-radius: var(--radius-xxs); }
.nav-tools-menu {
  list-style: none; margin: 0; padding: 6px;
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  min-width: 248px;
  background: var(--bg-elev); border: 0.5px solid var(--border); border-radius: 14px;
  box-shadow: 0 18px 44px rgba(0,0,0,0.28);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(-6px);
  transition: opacity .16s ease, transform .16s ease, visibility .16s;
}
.nav-tools-toggle:checked ~ .nav-tools-menu { opacity: 1; visibility: visible; pointer-events: auto; transform: none; }
.nav-tools-menu li { margin: 0; }
.nav-tools-menu a { display: flex; align-items: center; min-height: 44px; padding: 10px 12px; border-radius: var(--radius-xxs); color: var(--text); white-space: nowrap; }
.nav-tools-menu a:hover { background: var(--bg); text-decoration: none; }
.nav-tools[data-current] .nav-tools-label { color: var(--accent-ink); font-weight: 700; }
@media (max-width: 700px) {
  /* The panel becomes a list of lists. No disclosure, no caret, nothing to tap twice. */
  .nav-tools { display: flex; flex-direction: column; align-items: stretch; position: static; }
  .nav-tools-toggle { display: none; }   /* out of the tab order entirely on the phone */
  .nav-tools-label { display: none; }
  .nav-tools-link { display: flex; }
  .nav-tools-menu {
    /* INHERIT, never force. The desktop rules above set visibility:hidden and
       pointer-events:none on this list while it is closed; re-declaring them as
       visible/auto here overrode the CLOSED PANEL'S OWN hidden state, because a child
       setting pointer-events:auto beats a parent's none. The result was an invisible
       menu that still caught taps over the page after a back-navigation — measured, and
       caught by the check that exists for exactly that. Inheriting means this list is
       shown and tappable precisely when the panel is, and never otherwise.
       opacity is left at 1 deliberately: it is not inherited, and the panel's own
       opacity applies to its whole subtree regardless. */
    position: static; opacity: 1; visibility: inherit; pointer-events: inherit; transform: none;
    background: none; border: 0; box-shadow: none; border-radius: 0;
    padding: 0 0 4px 0; min-width: 0;
  }
  .nav-tools-menu a { padding-left: 30px; font-size: 0.95rem; color: rgba(255,255,255,0.88); }
  /* Eleven rows do not fit a short phone. Without this the last calculator sat below the
     fold of a fixed panel with no way to reach it — measured at 320x568. */
  header.site nav { max-height: calc(100vh - 74px); overflow-y: auto; -webkit-overflow-scrolling: touch; }
}
@media (prefers-reduced-motion: reduce) {
  .nav-tools-menu, .nav-caret { transition: none; }
}


/* "Coming soon" pill — AA contrast (darker in light mode; light mode only) */
.coming-soon .pill { color: #595960; }
@media (prefers-color-scheme: dark) { .coming-soon .pill { color: var(--text-dim); } }

/* Current-page nav indicator (you-are-here) */
header.site nav a[aria-current="page"] { color: var(--accent-ink); font-weight: 700; }
@media (max-width: 700px) {
  header.site nav a[aria-current="page"] { color: #fff; background: rgba(255,255,255,0.16); font-weight: 700; }
}

/* visually-hidden utility */
.visually-hidden { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Founder sign-off line (homepage founder block) */
.founder-sign { display: block; margin-top: var(--sp-2); font-size: var(--fs-small); color: var(--text-dim); font-style: italic; }

/* ===== Header "Open the app" CTA (board memo 2026-07-08, item 1) =====
   A persistent route to the product on every page. Desktop: a compact pill at
   the end of the header row. Phone (<=700px burger nav): the pill STAYS in the
   header bar beside the burger — a CTA that only exists behind a hamburger is
   not persistent — and repeats as the first item inside the dropdown. */
header.site .btn-app {
  order: 9; /* desktop: after the nav links, at the row's end */
  display: inline-flex; align-items: center;
  background: var(--btn-bg); color: var(--accent-text);
  font-weight: 600; font-size: 0.9rem; line-height: 1;
  padding: 9px 14px; border-radius: var(--radius-pill);
  min-height: 44px; white-space: nowrap;   /* Apple HIG minimum touch target */
}
header.site .btn-app:hover { text-decoration: none; filter: brightness(1.05); }
header.site nav .nav-app { display: none; } /* dropdown copy: phone only */
@media (max-width: 700px) {
  header.site .bar { gap: 8px; }
  header.site .btn-app { order: 0; margin-left: auto; font-size: 0.85rem; padding: 8px 12px; }
  header.site nav .nav-app { display: block; font-weight: 600; } /* colour inherited: the dropdown panel is dark, and --accent-ink on it measures 1.21:1 */
}

/* Guide bylines (board memo 2026-07-08, item 7): the trust wire to My Story. */
article .guide-byline { color: var(--text-dim); font-size: var(--fs-small); margin: -0.4rem 0 1.4rem; }

/* B4 (review 2026-07-08): the Tools page h1 sits inside .section-head, which only
   styled h2 — the h1 rendered at browser-default size, off the site's type scale. */
.section-head h1 { font-size: var(--fs-h2); margin: 0 0 var(--sp-2); }
/* B7: the FAQ's last two inline styles, promoted into the system. */
.updated.pull-up { margin-top: calc(-1 * var(--sp-3)); margin-bottom: var(--sp-4); }
.updated.spaced-top { margin-top: var(--sp-5); }

/* ===== App-frame component family =====
   Faithful reproductions of surfaces the app actually renders (spec §2 honesty
   rule, binding). Shapes and type mirror App/src/app.css: .card (bg-card,
   radius 12, padding 16), .card h2 (footnote size, 600, uppercase), .row
   (label regular / value dim right), .report-note, .row-sub. The .appframe
   container uses var(--bg) — the app's screen background — so cards sit on it
   the way they do in the app. Both color schemes come free via the tokens. */
.appframe {
  background: var(--bg);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  padding: 12px;
}
figure.appframe { margin: var(--sp-3) 0; }
.af-wrap { margin: var(--sp-3) 0; }
.af-card {
  background: var(--card);
  border-radius: 12px;
  padding: 14px 16px;
}
.af-card + .af-card { margin-top: 10px; }
/* Card caption — the app's .card h2 (small uppercase, NOT a page heading) */
.af-h {
  font-size: 0.8125rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.02em;
  color: var(--text-dim);
  margin: 0 0 10px;
}
/* Stat tiles — the app's Home/Compete classification tiles */
.af-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
/* App .stat tiles share the card's background (App/src/app.css:307) — faithful. */
.af-tile { background: var(--card); border-radius: 12px; padding: 14px 16px; }
.af-num { font-size: 1.75rem; font-weight: 700; line-height: 1.15; }
.af-pct { font-size: 15px; font-weight: 400; color: var(--text-dim); margin-left: 6px; }
.af-cap { font-size: 0.8125rem; color: var(--text-dim); margin-top: 2px; }
/* Rows — the app's .row list convention */
.af-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 6px 0; }
/* display:flex on .af-row beats the browser's [hidden]{display:none}; restore it —
   caught in the July 20 live verify: the true-cost fee row stayed visible at $0 fees. */
.appframe [hidden] { display: none !important; }
.af-row .af-label { flex: 1; }
.af-row .af-value { color: var(--text-dim); text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Compound values ("70 pts * 23.55s * HF 2.9724 * 70.6%") plus white-space:nowrap forced
   36px of horizontal scroll at 390px and 106px at the 320px WCAG 1.4.10 reflow width on
   guide-hit-factor.html. Below the phone breakpoint the value wraps instead. */
@media (max-width: 700px) {
  .af-row { flex-wrap: wrap; }
  .af-row .af-value { white-space: normal; min-width: 0; overflow-wrap: anywhere; }
}
.af-sub { display: block; font-size: 0.8125rem; color: var(--text-dim); margin: 2px 0 0; }
.af-tag { font-size: 11px; font-weight: 600; color: var(--text-dim); margin-left: 6px; text-transform: none; }
.af-tag.strong { color: var(--accent-ink); }
.af-note { font-size: var(--fs-small); color: var(--text-dim); margin-top: 10px; }
/* Bridge line + caption under a panel */
.appframe + .af-bridge, .af-bridge { font-size: var(--fs-small); color: var(--text-dim); margin: var(--sp-2) 0 0; }
figure.appframe figcaption { font-size: var(--fs-small); color: var(--text-dim); margin-top: 10px; }
.af-cta { margin-top: var(--sp-2); }

/* --- Published-data tables (added session 94) ---------------------------------
   The site's first tables. They exist because the numbers a search engine or an AI
   answer would want to quote — IDPA's 68 published class-time boundaries — lived only
   inside calc.js, where no crawler can reach them. A conditional warning that fires on
   a form state does not exist to a search engine; the same fact has to be on the page.
   Eight divisions x five classes does not fit 320px, so the table scrolls sideways in
   its own region rather than forcing the whole document to. The region is focusable and
   labelled, which is what makes a scrollable table reachable from a keyboard. */
/* CORRECTED session 95. Every rule below referenced --line, --panel, --ink, --ink-2 or
   --amber. NONE OF THOSE FIVE EXIST IN THIS FILE — they are defined only inside
   index.html's private stylesheet, and index.html carries no tables. So on all five pages
   that actually have one, every declaration using them was invalid at computed-value time
   and silently did nothing. Measured on the deployed site, both colour schemes:
     · .tbl-scroll border resolved to `none 0px` — the scrolling region had no boundary at
       all, so nothing on screen said it was a box with more inside it. That is not merely
       cosmetic: it is half the reason the hidden columns were undiscoverable.
     · every row rule resolved to 0px — an eight-by-five table with no lines in it.
     · .tbl-scroll:focus-visible resolved to `none 0px`, so a focusable element had NO
       visible focus indicator. WCAG 2.1 AA 2.4.7, on five pages, in both schemes.
     · the sticky header background resolved to transparent. Latent rather than live —
       overflow-x makes the region a scroll container on both axes and its height fits its
       content, so the header has no room to stick and never overlaps anything today. It
       would the moment a table grew taller than its region.
   The colours degraded more kindly: `color` is inherited, so an invalid value falls back
   to the inherited one and the text stayed full strength rather than vanishing. Nothing
   was ever unreadable; things were simply not dimmed as intended.
   The mapping is the nearest real token by ROLE, not a redesign: --line -> --hairline
   (identical values in both schemes), --panel -> --tag-bg (the shared system's subtly
   distinct surface), --ink -> --text, --amber -> --accent-ink (which is also what every
   other focus ring on this site uses, and it fixes a contrast problem the original had:
   --accent on white measures 2.4:1, under the 3:1 floor for a focus indicator).
   ONE JUDGEMENT CALL, named rather than buried: --ink-2 was #3a3a3c, a near-black, and
   --text-dim is genuinely dim. Data cells take --text, because dimming numbers a shooter
   is reading carefully is the wrong direction; the labels that are actually secondary
   (.tbl-abbr, .tbl-eff, .byline) take --text-dim, which is what they were reaching for. */
.tbl-scroll {
  overflow-x: auto; -webkit-overflow-scrolling: touch; margin-top: 10px;
  border: 1px solid var(--border); border-radius: 10px;
  /* THE SCROLL CUE. Every one of these tables hides between 38% and 64% of itself behind
     a sideways swipe at 390px — the peak-time table hides 64% — and nothing said so. The
     document does not overflow, because the table scrolls inside this region rather than
     pushing the page, which is why every 320/390 overflow check ever run reports clean and
     a screenshot looks perfect. It took a person on a phone being told to find something
     in the hidden half.
     Four background layers, no JavaScript. The two `local` gradients scroll WITH the
     content, so at the far left the first one covers the left shadow; the two `scroll`
     radial gradients are fixed to the box. The shadow at an edge is therefore visible
     exactly when there is content past it and gone when there is not — the state is
     rendered by the scroll position itself, with nothing to keep in sync. */
  background:
    linear-gradient(to right, var(--card) 40%, transparent) 0 0 / 34px 100% no-repeat local,
    linear-gradient(to left,  var(--card) 40%, transparent) 100% 0 / 34px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%,   var(--cue-shadow), transparent) 0 0 / 18px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, var(--cue-shadow), transparent) 100% 0 / 18px 100% no-repeat scroll,
    var(--card);
}
/* No .tbl-scroll:focus-visible rule, deliberately. The original said
   `outline: 2px solid var(--amber)` and drew nothing, because --amber does not exist in
   this file — that was the accessibility half of this session's defect. Repairing it to
   3px solid var(--accent-ink) turned out to reproduce, declaration for declaration, the
   global :focus-visible rule about six hundred lines above. The breakage proof is what
   surfaced it: deleting the rule entirely changed nothing measurable, three runs running,
   because the global rule was already doing the work. So the fix is subtraction. The
   check still asserts the resolved ring on the region; it simply comes from one place. */
.tbl { border-collapse: collapse; width: 100%; min-width: 560px; font-size: 15px; }
/* NOT FIXED, FLAGGED, AND SMALLER THAN IT FIRST LOOKED (session 95). A <caption> is as
   wide as its table, so a 659px table gives it a 659px line inside a 300px window and the
   text is cut off mid-sentence. MEASURED PER PAGE rather than assumed, and the first
   description of this was wrong: it affects exactly ONE table, on /steel-challenge-stages.
   The four calculator pages all mark their caption `visually-hidden` — it is their
   accessible name only, the human-readable context sits in prose above the table, and
   nothing is clipped because nothing is drawn. The new page is the odd one out.
   So this is not a stylesheet problem at all; it is one page departing from the site's own
   established pattern, and the repair is a content decision on that page -- Michael's, not
   an execution call. Two CSS-only workarounds were tried and BOTH are recorded as dead
   ends so nobody spends the hour again: `display:block; width:auto` fixed that page and
   left others alone only because their captions are already hidden -- it resolves against
   a different box depending on caption length, and a rule whose mechanism cannot be stated
   is next session's defect; and `container-type: inline-size` with `width: 100cqw` fixed
   it deterministically while widening the table 659px -> 839px, pushing the hidden
   proportion from 55% UP to 64% -- trading the symptom for the disease. */
.tbl caption { text-align: left; }
.tbl th, .tbl td { padding: 9px 12px; text-align: left; border-bottom: 1px solid var(--hairline); white-space: nowrap; }
.tbl thead th { font-weight: 600; color: var(--text); background: var(--tag-bg); position: sticky; top: 0; }
.tbl tbody th { font-weight: 600; color: var(--text); }
.tbl tbody tr:last-child th, .tbl tbody tr:last-child td { border-bottom: 0; }
.tbl td { color: var(--text); font-variant-numeric: tabular-nums; }
.tbl-abbr { color: var(--text-dim); font-weight: 400; }
.tbl-eff { font-weight: 400; color: var(--text-dim); font-size: .85em; }
.byline { color: var(--text-dim); font-size: 15px; margin-top: 6px; }
@media (max-width: 480px) { .tbl { font-size: 14px; } .tbl th, .tbl td { padding: 8px 10px; } }
/* Printing: the sticky header would repeat over the ink, a scroll region cannot scroll on
   paper, and a cue for a gesture nobody can make is ink spent on nothing. Let it lie flat. */
@media print { .tbl-scroll { overflow: visible; border: 0; background: none; } .tbl { min-width: 0; } .tbl thead th { position: static; } }
/* A cue that depends on a gradient is a cue some people will not get. When the user has
   asked the system for higher contrast, or the platform is painting its own palette, fall
   back to a hard rule on the two edges — a border cannot be tuned away. */
@media (prefers-contrast: more), (forced-colors: active) {
  .tbl-scroll { background: var(--card); border-left-width: 3px; border-right-width: 3px; }
}

/* The independence statement. Named organisations appear on nearly every page
   and link to their official sites; this is the one line that says we are not
   them. Recessive by design -- it is a fact the reader may need, not a claim
   we are making about ourselves. */
.foot-affil {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-dim);
  max-width: 78ch;
}
