/* cribbageweb — tokens and base styles. Source of truth: docs/DESIGN.md (W-0014).
   All sizes on the 4pt grid except the sanctioned 18px body. */

/* ---------- Fonts (self-hosted, latin subset — no CDN) ----------
   Linen Editorial faces (WP-1). Instrument Serif sets display headings only;
   Hanken Grotesk carries UI and prose; Spline Sans Mono sets uppercase labels. */
@font-face {
  font-family: 'Instrument Serif';
  src: url('/assets/fonts/instrument-serif-v5-latin-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Instrument Serif';
  src: url('/assets/fonts/instrument-serif-v5-latin-italic.woff2') format('woff2');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Hanken Grotesk';
  src: url('/assets/fonts/hanken-grotesk-v12-latin-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Hanken Grotesk';
  src: url('/assets/fonts/hanken-grotesk-v12-latin-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Hanken Grotesk';
  src: url('/assets/fonts/hanken-grotesk-v12-latin-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Hanken Grotesk';
  src: url('/assets/fonts/hanken-grotesk-v12-latin-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Spline Sans Mono';
  src: url('/assets/fonts/spline-sans-mono-v13-latin-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Spline Sans Mono';
  src: url('/assets/fonts/spline-sans-mono-v13-latin-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens (docs/DESIGN.md §Tokens) ----------
   Palette values come from the Linen Editorial handoff (WP-1); the pre-redesign
   legacy tokens were retired in WP-8 once the last consumer went away. Never set
   text in #8A8271 — it fails AA (~3.6:1). --ink-muted #6E675C is the muted text
   floor. */
:root {
  --paper: #ffffff;
  --linen: #f6f3ed; /* tinted bands, table header rows, card fills */
  --linen-bright: #fbf9f4; /* footer ground, highlighted table rows */
  --rule: #eee9de; /* light hairlines: row rules */
  --edge: #e5dfd3; /* structural hairlines: card, table and header borders */
  --ink: #1a1815; /* headings, primary text */
  /* --ink-body and --ink-soft collapsed onto --ink (W-0057): hierarchy in the
     reading column now comes from weight and size, not from tinting the text
     grey. The names stay so their consumers keep reading as what they are. */
  --ink-body: #1a1815; /* running prose */
  --ink-soft: #1a1815; /* leads, definitions, secondary prose */
  /* The one surviving tint layer — the mono small-caps chrome. It IS the
     hierarchy on that layer, so it does not collapse. */
  --ink-muted: #6e675c; /* mono labels, captions, metadata */
  --red: #c4382e; /* links, accents, primary CTA — 5.4:1 on paper */
  --red-hover: #a02d25;
  --crumb-sep: #c9c2b2; /* breadcrumb separator — decorative only */

  /* Hero bands — mirrored from the apps. The two landings, plus the footer (W-0025). */
  --felt-top: #0e1d36;
  --felt-bottom: #01050b;
  --nobs-ink: #f3f9ff;
  --nobs-accent: #5fa4f5; /* brighter than the old #2C8EF3 — AA on felt at small sizes */
  --nobs-lead: #c4d5e6; /* hero lead on felt — 11.2:1 on --felt-top */
  --mug-cream: #f2ecdc;
  --mug-navy: #23355c;
  --mug-red: #b23b34;
  --mug-lead: #3d4a6b; /* hero lead on cream — 8.2:1 on --mug-hero-top */
  --mug-band-ink: #c9d0e4; /* secondary line on the navy closing band — 7.9:1 */
  /* Muggins landing hero: a near-white cream wash that meets the page ground */
  --mug-hero-top: #faf7f0;
  --mug-hero-bottom: #fffdf9;

  /* Faces. --font-display is applied explicitly to .prose h1/h2 and nowhere
     else; --font-heading still styles nav, labels and buttons, which stay
     grotesk, so it aliases --font-ui rather than the serif. */
  --font-display: 'Instrument Serif', Georgia, serif;
  --font-ui: 'Hanken Grotesk', 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'Spline Sans Mono', ui-monospace, monospace;
  --font-heading: var(--font-ui);
  --font-body: var(--font-ui);

  /* Space & depth (docs/DESIGN.md §Space & depth). Full-bleed bands are never rounded. */
  --radius-card: 16px;
  --radius-figure: 12px;
  --radius-pill: 999px;
  /* Shadows are rare and soft, and tinted with --ink rather than the retired
     navy (handoff §Spacing): card slot, then screenshot lift. */
  --shadow-soft: 0 2px 8px rgba(26, 24, 21, 0.05);
  --shadow-lift: 0 8px 24px rgba(26, 24, 21, 0.1);
  --shadow-cta: 0 6px 18px rgba(196, 56, 46, 0.22); /* red pill glow */
  /* The ink-tinted shadows above vanish on the felt bands. This one is tinted
     with --felt-bottom and carries far more depth, so a white card reads as
     lifted off the navy rather than pasted onto it (W-0060). */
  --shadow-onfelt: 0 12px 32px rgba(1, 5, 11, 0.55);
}

/* ---------- Reset-ish ---------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}
html {
  height: 100%;
}
body {
  margin: 0;
  min-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--ink-body);
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 30px;
  -webkit-text-size-adjust: 100%;
}
img,
video {
  max-width: 100%;
  height: auto;
}

/* ---------- Skip link ---------- */
/* First focusable thing on every page. Clipped until focused, then pinned to
   the viewport corner so it lands above the header at any scroll position. */
.skip-link {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.skip-link:focus {
  position: fixed;
  z-index: 100;
  top: 8px;
  left: 8px;
  width: auto;
  height: auto;
  margin: 0;
  padding: 12px 16px;
  clip-path: none;
  background: var(--paper);
  border: 1px solid var(--edge);
  border-radius: var(--radius-figure);
  color: var(--ink);
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 16px;
  line-height: 20px;
  text-decoration: none;
}
/* The skip target is a wrapper div, focused programmatically by the browser. */
#content:focus {
  outline: none;
}

/* ---------- Header ---------- */
/* `position: relative` is the anchor for the mobile nav panel; article pages
   upgrade it to `sticky` via .site-header--article (see Reading header). */
.site-header {
  position: relative;
  background: var(--paper);
  border-bottom: 1px solid var(--edge);
}
/* Grid, not flex, so the nav and the reading crumb can share one center cell
   and swap with zero layout shift. Four columns only when the hamburger is
   actually displayed — an empty auto track would still contribute its gap. */
.site-header__inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 24px 32px;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 24px;
}
.site-header__brand {
  display: inline-flex;
  align-items: center;
  padding: 12px 0; /* 48px row — clears the 44px tap target */
  text-decoration: none;
}
.site-header__brand img {
  display: block;
  height: 28px;
  width: auto;
}
.site-nav {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* No at-rest underline: a row of nothing but links doesn't need one (W-0025).
   The active item is the exception — it earns a red underline. */
.site-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 12px;
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 16px;
  line-height: 20px;
  color: var(--ink-muted);
  text-decoration: none;
}
.site-nav a:hover,
.site-nav a:focus-visible {
  color: var(--ink);
}
.site-nav a[aria-current] {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--red);
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}
/* Row furniture for the mobile panel — both are dead weight in the desktop nav
   row, so they default to hidden and the panel turns them on. */
.site-nav__arrow {
  display: none;
  flex: none;
}
.site-nav__actions {
  display: none;
}

/* Menu-foot pills (mobile panel only). "Get Nobs" is a non-interactive <span>
   by design: Nobs is unreleased, so there is nowhere to send anyone (BRAND.md).
   It reads as an off state — linen fill, hairline border, muted ink.
   Every selector here is prefixed with .site-nav__actions on purpose: the
   `.site-nav a` row above is one class plus one element, so a bare .nav-pill
   would lose the colour and the type to it. */
.site-nav__actions .nav-pill {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 56px;
  padding: 12px 20px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 18px;
  line-height: 24px;
  text-align: center;
  text-decoration: none;
}
.site-nav__actions .nav-pill--disabled {
  background: var(--linen);
  border: 1px solid var(--rule);
  color: var(--ink-muted);
  cursor: default;
}
.site-nav__actions .nav-pill__note {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.site-nav__actions .nav-pill--primary {
  background: var(--red);
  border: 1px solid var(--red);
  color: var(--paper);
}
.site-nav__actions .nav-pill--primary:hover,
.site-nav__actions .nav-pill--primary:focus-visible {
  background: var(--red-hover);
  border-color: var(--red-hover);
  color: var(--paper);
}

/* Outlined pill — the header's one commercial affordance. */
.cta-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 12px 24px;
  background: var(--paper);
  border: 1px solid var(--ink);
  border-radius: var(--radius-pill);
  color: var(--ink);
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 16px;
  line-height: 20px;
  text-decoration: none;
  white-space: nowrap;
}
.cta-pill:hover,
.cta-pill:focus-visible {
  background: var(--ink);
  color: var(--paper);
}

/* Hamburger. Ships with the `hidden` attribute and is only revealed once
   site.js has confirmed it can drive the panel — see .js-nav below. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--ink);
  cursor: pointer;
}
.nav-toggle__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* Three bars rather than an icon swap, so the hamburger can rotate into the X
   that closes the panel. aria-expanded is the state source — the same attribute
   assistive tech reads — so nothing can drift out of sync. Under reduced motion
   the global transition kill turns this into an instant swap, which is fine. */
.nav-toggle__bars {
  position: relative;
  display: block;
  width: 24px;
  height: 16px;
}
.nav-toggle__bar {
  position: absolute;
  left: 0;
  width: 24px;
  height: 2px; /* stroke — grid-exempt */
  background: currentColor;
  border-radius: 2px;
  transition:
    transform 0.2s ease,
    opacity 0.16s ease;
}
.nav-toggle__bar:nth-child(1) {
  top: 0;
}
.nav-toggle__bar:nth-child(2) {
  top: 7px; /* centres the 2px bar in the 16px box — computed, grid-exempt */
}
.nav-toggle__bar:nth-child(3) {
  top: 14px;
}
.nav-toggle[aria-expanded='true'] .nav-toggle__bar:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.nav-toggle[aria-expanded='true'] .nav-toggle__bar:nth-child(2) {
  opacity: 0;
}
.nav-toggle[aria-expanded='true'] .nav-toggle__bar:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* Mobile nav. Gated on BOTH the width and the .js-nav class site.js sets on
   <html>: with JS off there is no way to reopen a collapsed nav, so it stays
   inline and wraps, which is what the header did before this package. */
/* The phone bar is two things: the wordmark and the hamburger (W-0057). The
   pill and the reading crumb both go — the pill lives in the menu panel and in
   the page, and the crumb never had the width to say anything useful. The 2px
   progress rule stays. */
@media (max-width: 719px) {
  .site-header__inner {
    grid-template-columns: auto minmax(0, 1fr) auto;
  }
  .cta-pill {
    display: none;
  }
  .js-nav .nav-toggle {
    display: inline-flex;
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
  }
  .js-nav .site-nav {
    display: none;
  }
  /* Open: a white sheet from the foot of the bar to the foot of the screen.
     `fixed`, not `absolute`, so the panel cannot scroll away with the page;
     --bar-h is measured by site.js at the moment of opening. */
  .js-nav .site-header.nav-open .site-nav {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    justify-content: flex-start;
    gap: 0;
    position: fixed;
    z-index: 30;
    top: var(--bar-h, 64px);
    left: 0;
    right: 0;
    bottom: 0;
    padding: 0 0 32px;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--paper);
  }
  /* Rows, not links: the whole width is the target, and the arrow says where it
     goes. The active page is red — an underline reads as decoration at 28px. */
  .js-nav .site-header.nav-open .site-nav > a {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    min-height: 0;
    padding: 32px 20px;
    font-family: var(--font-ui);
    font-weight: 500;
    font-size: 28px;
    line-height: 36px;
    color: var(--ink);
    text-decoration: none;
    border-bottom: 1px solid var(--rule);
  }
  .js-nav .site-header.nav-open .site-nav > a[aria-current] {
    color: var(--red);
    text-decoration: none;
  }
  .js-nav .site-header.nav-open .site-nav__arrow {
    display: block;
  }
  .js-nav .site-header.nav-open .site-nav__actions {
    display: flex;
    align-items: stretch;
    gap: 12px;
    margin-top: 32px;
    padding: 0 20px;
  }
  /* Scroll lock. Scoped to this query so a resize past the breakpoint can never
     leave the document stuck. */
  html.nav-lock,
  html.nav-lock body {
    overflow: hidden;
  }
}
@media (max-width: 767px) {
  .site-header__inner {
    padding: 16px 20px;
  }
}

/* ---------- Reading header ---------- */
/* Article pages only (`readingHeader: true` on the three article layouts). The
   bar sticks; once the h1 has scrolled past it, site.js adds .is-condensed and
   the centre cell swaps the nav for "Section › Page title" while a 2px progress
   rule appears on the bottom border. Both center-cell children live in the same
   grid cell, so the swap moves nothing. */
.site-header--article {
  position: sticky;
  top: 0;
  z-index: 20;
}

.site-header__crumb {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.16s ease,
    visibility 0s 0.16s;
}
/* Uniform crumb (W-0057): one face, one size, one weight across both halves.
   Position is signalled by colour alone — muted section, ink title. */
.site-header__crumb-section {
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 16px;
  line-height: 20px;
  color: var(--ink-muted);
  text-decoration: none;
  white-space: nowrap;
}
.site-header__crumb-section:hover,
.site-header__crumb-section:focus-visible {
  color: var(--red);
}
.site-header__crumb-sep {
  font-size: 16px;
  line-height: 20px;
  color: var(--crumb-sep);
}
.site-header__crumb-title {
  min-width: 0;
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 16px;
  line-height: 20px;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Condensed state. `visibility` (not opacity alone) is what takes the hidden
   half out of the tab order and the accessibility tree. */
.site-nav {
  transition:
    opacity 0.16s ease,
    visibility 0s 0s;
}
.site-header--article.is-condensed .site-nav {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.16s ease,
    visibility 0s 0.16s;
}
.site-header--article.is-condensed .site-header__crumb {
  opacity: 1;
  visibility: visible;
  transition:
    opacity 0.16s ease,
    visibility 0s 0s;
}
/* …except when the mobile panel is open: that IS the nav. */
.site-header--article.is-condensed.nav-open .site-nav {
  opacity: 1;
  visibility: visible;
}
.site-header--article.is-condensed .site-header__inner {
  padding-top: 12px;
  padding-bottom: 12px;
}
.site-header--article.is-condensed .site-header__brand img {
  height: 24px;
  width: auto;
}

/* Progress rule. Absolutely positioned over the header's own bottom border, so
   its arrival costs no layout. */
.read-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--rule);
  opacity: 0;
}
.site-header--article.is-condensed .read-progress {
  opacity: 1;
}
.read-progress__fill {
  height: 100%;
  background: var(--red);
  transform-origin: 0 50%;
  transform: scaleX(0);
  will-change: transform;
}

/* Where the browser supports scroll-driven animations the fill is driven
   entirely by CSS — no scroll listener runs at all (see initReadingProgress).
   The `~ #content` combinator scopes the timeline to article pages: only they
   render a .site-header--article, and each renders exactly one article.prose. */
@supports (animation-timeline: --a) and (timeline-scope: --a) {
  body {
    timeline-scope: --reading-article;
  }
  .site-header--article ~ #content article.prose {
    view-timeline: --reading-article block;
  }
  .read-progress__fill {
    animation: read-progress-grow auto linear both;
    animation-timeline: --reading-article;
    animation-range: contain 0% contain 100%;
  }
}
@keyframes read-progress-grow {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

/* Phones never see the crumb (W-0057). A 375px bar has no width to say anything
   useful once the wordmark and the hamburger have taken theirs, and the reader
   already has the page's own breadcrumbs a scroll away. The condensed bar there
   is the wordmark, the hamburger and the progress rule — nothing else. This has
   to sit after the .site-header__crumb rule above, not up in the ≤719 header
   block, or the base `display: flex` wins on source order. */
@media (max-width: 719px) {
  .site-header__crumb {
    display: none;
  }
}

/* ---------- Footer ---------- */
/* Light footer (WP-2). The felt gradient and its two app cards are gone: the
   apps are represented by their link columns, and the global red focus ring
   works on this ground, so the old --sky outline override goes with them. */
.site-footer {
  margin-top: auto;
  background: var(--linen-bright);
  border-top: 1px solid var(--edge);
  color: var(--ink);
}
.site-footer__inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 64px 32px 24px;
}

/* Brand block + link columns */
.site-footer__cols {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 48px;
}
.site-footer__brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  max-width: 340px;
}
.site-footer__mark {
  display: block;
  height: 24px;
  width: auto;
}
.site-footer__blurb {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 16px;
  line-height: 24px;
  color: var(--ink-soft);
  margin: 0;
  text-wrap: pretty;
}
/* 12 + 20 + 12 = a 44px tap target without a visible box around the address. */
.site-footer__email {
  display: inline-block;
  padding: 12px 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 20px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--red);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 5px;
}
.site-footer__email:hover,
.site-footer__email:focus-visible {
  color: var(--red-hover);
  text-decoration-thickness: 2px;
}
/* Column labels are <p>, not headings: three more <h2>s on every page would
   muddy each page's outline. <nav aria-labelledby> keeps the grouping for AT. */
.site-footer__head {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 8px;
}
/* The longest label, "Printable cheat sheets", measures 159px in Hanken 400
   16px. 176 keeps it on one line with headroom; a longer label than that one
   needs this re-checked. */
.site-footer__col {
  min-width: 176px;
}
.site-footer__col ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* No underline at rest: a column of nothing but links doesn't need one to be
   read as links, and the underlines shout over the labels (W-0025). */
.site-footer__col a {
  display: inline-block;
  padding: 12px 0; /* 48px row — clears the 44px tap target */
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 16px;
  line-height: 24px;
  color: var(--ink);
  text-decoration: none;
}
.site-footer__col a:hover,
.site-footer__col a:focus-visible {
  color: var(--red);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 5px;
}

/* Bottom row: copyright left, serif flourish right */
.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-top: 48px;
  border-top: 1px solid var(--rule);
  padding-top: 20px;
}
.site-footer__legal p {
  margin: 0;
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 14px;
  line-height: 20px;
  color: var(--ink-muted);
}
.site-footer__legal .site-footer__flourish {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 18px;
  line-height: 24px;
  color: var(--ink-muted);
}
@media (max-width: 560px) {
  .site-footer__inner {
    padding: 48px 20px 24px;
  }
  .site-footer__cols {
    gap: 32px;
  }
}

/* ---------- Page shells ---------- */
.page {
  max-width: 1180px;
  margin: 0 auto;
  padding: 72px 32px 112px;
  width: 100%;
}
@media (max-width: 767px) {
  .page {
    padding: 72px 20px 64px;
  }
}
/* Article pages: TOC rail left, article right (docs/DESIGN.md §Layout, WP-4).
   The article comes FIRST in the DOM — reading order is the article, then its
   index — and the grid moves the rail into column 1. `align-items: start` is
   load-bearing: a stretched grid item cannot be sticky. */
.page--article {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: 48px 72px;
  align-items: start;
}
.page--article > .prose {
  grid-column: 2;
  grid-row: 1;
}
.page--article > .rail {
  grid-column: 1;
  grid-row: 1;
  align-self: start;
}
/* Below 960 the rail stacks after the article, in source order. */
@media (max-width: 959px) {
  .page--article {
    display: block;
  }
  .page--article > .rail {
    margin-top: 48px;
  }
}

/* ---------- Prose ---------- */
.prose {
  max-width: 62ch;
}
.prose h1,
.prose h2,
.prose h3 {
  color: var(--ink);
  text-wrap: balance;
  margin: 48px 0 16px;
}
/* The display serif appears here and nowhere else on the site. */
.prose h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 60px;
  line-height: 68px;
  letter-spacing: 0;
  margin-top: 24px;
}
.prose h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 36px;
  line-height: 44px;
  letter-spacing: 0;
  margin: 56px 0 16px;
}
.prose h3 {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 20px;
  line-height: 28px;
}
/* Anchored sections: a jump must not land the heading under the sticky reading
   header, which is ~71px condensed. */
.prose h2[id] {
  scroll-margin-top: 96px;
}
@media (max-width: 767px) {
  .prose h1 {
    font-size: 44px;
    line-height: 52px;
  }
}
@media (max-width: 479px) {
  .prose h1 {
    font-size: 36px;
    line-height: 44px;
  }
  .prose h2 {
    font-size: 28px;
    line-height: 36px;
  }
}
.prose p,
.prose ul,
.prose ol {
  font-size: 18px;
  line-height: 30px;
  color: var(--ink-body);
  margin: 0 0 16px;
}
.prose li {
  margin-bottom: 8px;
}
/* With the body ink at full strength (W-0057), emphasis has to come from weight
   alone — the browser default 'bolder' lands on 600 here and barely reads. */
.prose strong,
.prose b {
  font-weight: 700;
}
.prose a {
  color: var(--red);
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}
.prose a:hover,
.prose a:focus-visible {
  color: var(--red-hover);
  text-decoration-thickness: 2px;
}
.prose .lead {
  font-size: 20px;
  line-height: 36px;
  color: var(--ink-soft);
}
/* `.prose p` is one point heavier than a bare `.eyebrow`, so inside an article
   the eyebrow needs the second selector or it renders as 18/30 body ink (this
   is what /404 and /add do). */
.eyebrow,
.prose .eyebrow {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--red);
  margin: 0 0 8px;
}

/* Breadcrumb — the visible trail the BreadcrumbList schema claims (W-0029).
   Inherits the eyebrow's type treatment, since it replaced the eyebrow on
   content pages and the header should not shift weight. Navigation, not prose,
   so no at-rest underline (the W-0025 footer rule); hover and focus restore it. */
.crumbs {
  margin: 0 0 8px;
}
.crumbs ol,
.prose .crumbs ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.crumbs li,
.prose .crumbs li {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  color: var(--ink-muted);
}
.crumbs li + li::before {
  content: '\203A'; /* › */
  color: var(--crumb-sep);
  font-weight: 400;
  letter-spacing: 0;
}
.crumbs a,
.prose .crumbs a {
  color: var(--ink-muted);
  text-decoration: none;
}
.crumbs a:hover,
.crumbs a:focus-visible,
.prose .crumbs a:hover,
.prose .crumbs a:focus-visible {
  color: var(--red);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.prose figcaption,
.prose .meta,
.prose .reviewed {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
/* The trust line always sets as two lines, with air on both sides — it is the
   seam between the article and whatever follows it (W-0057). */
.prose .reviewed {
  margin: 32px 0;
}
.prose .reviewed span {
  display: block;
}
/* "← All cribbage terms" — the only way back out of a glossary entry, so it is
   sized to be seen rather than tucked under the trust line. */
.prose .back-link {
  margin: 32px 0 0;
}
.prose .back-link a {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 18px;
  line-height: 28px;
}

/* Tables: the scoring workhorse. The contained table — a bordered, radiused
   box that still scrolls sideways on a phone. The box lives on the wrapper so
   the radius clips the linen header band; cells carry no outer borders. */
.prose .table-scroll {
  overflow-x: auto;
  max-width: 100%;
  border: 1px solid var(--edge);
  border-radius: var(--radius-card);
  /* Tables need room to read as their own object, not as a run of paragraphs.
     48 is the scale step at or above the 36 minimum the owner asked for. */
  margin: 48px 0;
}
.prose table {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
  font-size: 16px;
  line-height: 26px;
  font-variant-numeric: tabular-nums;
  margin: 48px 0;
}
.prose .table-scroll > table {
  margin: 0;
}
.prose th,
.prose td {
  text-align: left;
  padding: 16px 20px;
}
.prose td {
  font-family: var(--font-ui);
  border-top: 1px solid var(--rule);
}
.prose th {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 20px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-muted);
  background: var(--linen);
}

/* Callout — one per page, or none */
.callout {
  background: var(--linen);
  border: 1px solid var(--edge);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-soft);
  padding: 24px 28px;
  margin: 24px 0;
}

/* App CTA button — the site's one primary action: a red filled pill with the
   red glow. `inline-flex` + 16/32 padding on a 24px line is exactly the 56px
   min-height the handoff asks for, so the box never has to be told twice. The
   two app-palette variants (--navy, --cream) override the fill and drop the
   glow; both live in the marketing block below. */
.cta-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 18px;
  line-height: 24px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--paper);
  background: var(--red);
  text-decoration: none;
  padding: 16px 32px;
  min-height: 56px;
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-cta);
}
.cta-button:hover,
.cta-button:focus-visible {
  background: var(--red-hover);
}
/* A CTA sitting inside body copy. `.prose a` and `.mkt p a` are each one
   specificity point heavier than `.cta-button`, so without this the button
   inherits the body link colour and renders --red ink on a --red fill:
   invisible. `/` previously dodged this by never wrapping its CTA in a <p>;
   that constraint is no longer load-bearing. Ink on the solid red and navy
   fills is always --paper — the cream pill is the one exception, re-set beside
   its own rule in the marketing block. */
.prose a.cta-button,
.mkt p a.cta-button {
  color: var(--paper);
  text-decoration: none;
}
.prose a.cta-button:hover,
.prose a.cta-button:focus-visible,
.mkt p a.cta-button:hover,
.mkt p a.cta-button:focus-visible {
  color: var(--paper);
  text-decoration: none;
}
/* ---------- Hub pages (glossary, rules, tools, legal) ---------- */
/* One display step above an article h1 — the handoff's 76/80 hub line. Gated to
   960px and up on purpose: below that the article h1 steps (44/52 → 36/44) are
   already right, and this selector is heavier than they are, so an ungated rule
   would leave 76px running off the side of a phone. */
@media (min-width: 960px) {
  .page--hub .prose h1 {
    font-size: 76px;
    line-height: 80px;
    letter-spacing: -0.01em;
    max-width: 18ch;
  }
}
/* The glossary and legal hubs put a 280px label column beside their term lists,
   which does not fit inside the 62ch reading measure. Those two open the prose
   column to the full 1180px container and move the measure cap onto the running
   text instead. The rules and tools hubs are single-column and keep 62ch. */
.prose--wide {
  max-width: none;
}
.prose--wide > p {
  max-width: 62ch;
}
.prose--wide > .lead {
  max-width: 56ch;
}

/* Glossary hub: jump-nav term index — a hairline-ruled band of group links. */
.term-index {
  list-style: none;
  margin: 48px 0 0;
  /* 8px here plus 12px on each link reads as the handoff's 20px rule-to-text
     padding while giving every link a 44px tap target. */
  padding: 8px 0;
  border-top: 1px solid var(--edge);
  border-bottom: 1px solid var(--edge);
  display: flex;
  flex-wrap: wrap;
  gap: 0 28px;
  max-width: none;
}
.term-index li {
  margin: 0;
}
.term-index a {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 12px 0;
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 16px;
  line-height: 20px;
  color: var(--ink);
  text-decoration: none;
}
.term-index .num {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.12em;
  color: var(--red);
}
.term-index a:hover,
.term-index a:focus-visible {
  color: var(--red);
  text-decoration: underline;
  text-underline-offset: 5px;
}
/* On a phone the wrapped band turns into a thumb-sized list — one group per
   full-width row, hairline-separated, the same vocabulary as the menu panel
   (W-0057). 12 + 20 + 12 is the 44px target. */
@media (max-width: 719px) {
  .term-index {
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 0;
    padding: 0;
  }
  .term-index li + li {
    border-top: 1px solid var(--rule);
  }
  .term-index a {
    padding: 12px 0;
  }
}
.prose section {
  scroll-margin-top: 24px;
}

/* Glossary and legal hubs: a group is a numbered serif label in the left column
   and its definition list in the right. Single column below 960px. */
.term-group {
  margin-top: 72px;
}
@media (min-width: 960px) {
  .term-group {
    display: grid;
    grid-template-columns: 280px minmax(0, 1fr);
    gap: 48px;
    align-items: start;
  }
}
.prose .term-group__num {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--red);
  margin: 0 0 8px;
}
.prose .term-group h2 {
  font-size: 36px;
  line-height: 40px;
  margin: 0;
}
/* The `:not()` keeps this off the number above it — a bare `.prose … p` would
   otherwise outweigh `.prose .term-group__num`. */
.prose .term-group__head p:not(.term-group__num) {
  font-size: 16px;
  line-height: 24px;
  color: var(--ink-soft);
  margin: 16px 0 0;
}
.prose .term-group__body p {
  max-width: 62ch;
  margin: 0;
}
@media (max-width: 959px) {
  .term-group__head {
    margin-bottom: 24px;
  }
}
.term-group + .reviewed {
  margin-top: 72px;
}

/* Glossary hub: grouped one-line definitions (dl > div > dt + dd) */
.term-list {
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--edge);
}
/* The rules and tools hubs use the same list without the label column. */
.prose > .term-list {
  margin: 32px 0 48px;
}
.term-row {
  padding: 16px 0;
  border-bottom: 1px solid var(--rule);
  scroll-margin-top: 24px;
}
.term-row dt {
  display: inline;
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 18px;
  color: var(--ink);
}
.term-row dt a {
  color: var(--red);
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}
.term-row dt a:hover,
.term-row dt a:focus-visible {
  color: var(--red-hover);
  text-decoration-thickness: 2px;
}
.term-row dd {
  display: inline;
  font-size: 18px;
  line-height: 28px;
  color: var(--ink-soft);
  margin: 0;
}
.term-row .syn {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 18px;
  color: var(--ink-muted);
}

/* Content figures — a real image inside an article column. Same treatment as the
   marketing screenshots (.mkt-theme img). No --edge outline: these images carry
   their own ground, and a generic rule can't know whether it is light or dark. */
.prose figure {
  margin: 24px 0;
}
.prose figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 8px; /* editorial figures stay quiet — no border */
  box-shadow: var(--shadow-lift);
}

/* Image placeholder — linen box marking where owner-created imagery will go.
   Replace the whole <figure> with the real image when it exists. */
.img-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 3 / 2;
  background: var(--linen);
  border: 2px dashed var(--edge); /* stroke — grid-exempt */
  border-radius: var(--radius-card);
  margin: 24px 0;
  padding: 24px;
  text-align: center;
}
.img-placeholder figcaption {
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 20px;
  color: var(--ink-muted);
}

/* ---------- Article rail (WP-4) ----------
   No box: the rail is type on the page ground, held together by one hairline the
   TOC items hang off. Two groups, in this order — the section index, then the
   related links. */
.rail__label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 16px;
}
.rail ol,
.rail ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.rail li {
  margin: 0;
}
/* Section index. The items hang off a hairline; the active one overlaps it with a
   2px red border, which is why the link carries the negative margin. */
.rail__toc ol {
  border-left: 1px solid var(--edge);
}
.rail__toc li {
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 20px;
}
.rail__toc a {
  display: block;
  color: var(--ink-muted);
  text-decoration: none;
  padding: 4px 0 4px 16px;
  border-left: 2px solid transparent;
  margin-left: -1px;
}
.rail__toc a:hover,
.rail__toc a:focus-visible {
  color: var(--ink);
}
/* Static active state: the reader starts at the top of the document. Scroll
   spying would need JS, and the site's JS budget is spent (W-0014). */
.rail__toc li:first-child a {
  color: var(--red);
  border-left-color: var(--red);
}
/* The section index is desktop furniture (W-0057). Stacked under the article it
   is a second table of contents for a page the reader has already read, so on a
   phone it goes and the related links stand alone. */
@media (max-width: 959px) {
  .rail__toc {
    display: none;
  }
}
/* Related links. 12 + 20 + 12 = a 44px tap target with no visible box. The gap is
   the one between the two groups, so it goes when the links stand alone (the
   calculator rail, and any article without section headings). */
.rail__related {
  margin-top: 48px;
}
.rail__related:first-child {
  margin-top: 0;
}
.rail__related a {
  display: block;
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 20px;
  padding: 12px 0;
  color: var(--red);
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}
.rail__related a:hover,
.rail__related a:focus-visible {
  color: var(--red-hover);
  text-decoration-thickness: 2px;
}
/* Stacked after the article the related links are the page's next step, not a
   sidebar footnote — so they get read-size type and their own label grows with
   them. The top gap goes with the TOC that used to sit above them. */
@media (max-width: 959px) {
  .rail__related {
    margin-top: 0;
  }
  .rail__label {
    font-size: 14px;
    line-height: 20px;
  }
  .rail__related a {
    font-size: 18px;
    line-height: 28px;
    padding: 8px 0;
  }
}
/* The index follows the reader down the page. 96 clears the ~71px condensed
   reading header. Below 960 the rail has already stacked after the article,
   where sticking would be wrong. */
@media (min-width: 960px) {
  .page--article .rail {
    position: sticky;
    top: 96px;
    max-height: calc(100vh - 120px);
    overflow-y: auto;
  }
}

/* ---------- Legal documents (W-0033) ----------
   One template serves both apps' policies. The meta row is what answers "which
   app is this, and how current is it?" before any legal text is read; the rail
   variant turns the related-links column into the document's section index.
   No new tokens and no legal-only type sizes: .prose carries the document body
   exactly as it carries every other page. */
.legal-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 16px 56px;
  margin: 0 0 32px;
  padding: 0 0 24px;
  border-bottom: 1px solid var(--rule);
}
.legal-meta > div {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.legal-meta__label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.legal-meta__value {
  font-family: var(--font-ui);
  font-size: 18px;
  line-height: 28px;
  color: var(--ink);
}
.legal-crosslink {
  margin: 48px 0 0;
  padding: 24px 0 0;
  border-top: 1px solid var(--rule);
}
.legal-crosslink p {
  font-size: 16px;
  line-height: 24px;
  color: var(--ink-soft);
  margin: 0 0 8px;
}
.legal-crosslink p:last-child {
  margin-bottom: 0;
}

/* ---------- Marketing landings ----------
   `.mug-hero` and `.qr-pop` are Muggins-only. `.mkt*` is the shared marketing
   layer: /muggins/, /printables/ and / all put it on their <main>.

   Linen Editorial vocabulary (WP-7): serif display headings, mono uppercase
   labels, hairlined linen bands, one red pill. The two hero bands are the
   exception — each app keeps its own palette there, because the band is that
   app's one branded zone on this site (W-0023 for /muggins/, W-0031 for /).
   Full-bleed bands are never rounded.

   Rules that land inside <main class="mkt"> are written `.mkt .thing` or
   `.mkt p.thing` (0-2-x) so they beat `.mkt h2` / `.mkt p` (0-1-1) on
   specificity rather than on source order. `.mkt-intro` is the cautionary
   tale: its 32px bottom margin was silently dead for two work packages. */

.mug-hero {
  background: linear-gradient(to bottom, var(--mug-hero-top), var(--mug-hero-bottom));
  color: var(--mug-navy);
  padding: 88px 32px 0;
  text-align: center;
}
.mug-hero__inner {
  max-width: 1180px;
  margin: 0 auto;
}
.mug-hero .eyebrow {
  color: var(--mug-red);
  margin: 0 0 20px;
}
.mug-hero h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 76px;
  line-height: 80px;
  letter-spacing: -0.01em;
  color: var(--mug-navy);
  text-wrap: balance;
  margin: 0 0 20px;
}
/* The one accent word, italic in the app's red. */
.mug-hero h1 em {
  font-style: italic;
  color: var(--mug-red);
}
.mug-hero__lead {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 20px;
  line-height: 32px;
  color: var(--mug-lead);
  max-width: 42ch;
  margin: 0 auto;
  text-wrap: pretty;
}
.mug-hero__cta {
  margin: 32px 0 0;
}
/* Sits on the bottom edge of the band, no gap below it */
.mug-hero__art {
  display: block;
  width: 100%;
  max-width: 1050px;
  height: auto;
  margin: 48px auto 0;
}

/* Download CTA variants. The base .cta-button is the red pill; these two carry
   the Muggins palette, so they also drop the red glow that goes with it. */
.cta-button--navy,
.cta-button--cream {
  box-shadow: none;
}
.cta-button--navy {
  background: var(--mug-navy);
  color: var(--paper);
}
.cta-button--navy:hover,
.cta-button--navy:focus-visible {
  background: #1a2745; /* one step darker than --mug-navy — hover only */
}
.cta-button--cream {
  background: var(--mug-cream);
  color: var(--mug-navy);
}
.cta-button--cream:hover,
.cta-button--cream:focus-visible {
  background: var(--paper);
}
/* The cream pill is the one CTA whose ink is not --paper, and the blanket
   `a.cta-button` colour reset above (0-2-2, written for the solid red and navy
   fills) outranks the variant. Without this it renders white on cream — which
   only ever showed on touch, where the store link replaces the QR button. */
.prose a.cta-button--cream,
.prose a.cta-button--cream:hover,
.prose a.cta-button--cream:focus-visible,
.mkt p a.cta-button--cream,
.mkt p a.cta-button--cream:hover,
.mkt p a.cta-button--cream:focus-visible {
  color: var(--mug-navy);
}
button.cta-button {
  border: 0;
  cursor: pointer;
}
/* Touch gets the store link; a real pointer gets the QR panel instead */
.cta-qr {
  display: none;
}
@media (hover: hover) and (pointer: fine) {
  .cta-store {
    display: none;
  }
  .cta-qr {
    display: inline-flex;
  }
}

/* QR panel: native Popover API, no script */
.qr-pop {
  width: 320px;
  max-width: calc(100% - 40px);
  inset: 0;
  margin: auto;
  padding: 32px;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--edge);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-lift);
  text-align: center;
}
.qr-pop::backdrop {
  background: rgba(26, 24, 21, 0.4); /* ink scrim, mirroring the shadow tint */
}
.qr-pop__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 28px;
  line-height: 36px;
  color: var(--ink);
  margin: 0 0 16px;
}
.qr-pop__code {
  display: block;
  width: 200px;
  height: 200px;
  margin: 0 auto;
  padding: 8px;
  background: var(--paper);
  border-radius: var(--radius-figure);
}
.qr-pop__hint {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 16px;
  line-height: 24px;
  color: var(--ink-soft);
  margin: 16px 0 0;
}
.qr-pop__link {
  display: inline-block;
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 16px;
  line-height: 24px;
  color: var(--red);
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
  padding: 12px 0;
  min-height: 44px;
}
.qr-pop__link:hover,
.qr-pop__link:focus-visible {
  color: var(--red-hover);
  text-decoration-thickness: 2px;
}
.qr-pop__close {
  display: block;
  width: 100%;
  min-height: 44px;
  margin: 8px 0 0;
  padding: 12px 16px;
  border: 0;
  background: none;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-muted);
  cursor: pointer;
}
.qr-pop__close:hover,
.qr-pop__close:focus-visible {
  color: var(--ink);
}

/* Marketing sections */
.mkt-band {
  padding: 96px 32px;
}
/* The tinted band. Hairlines top and bottom: on a white page the fill alone is
   too quiet to read as a band edge. */
.mkt-band--linen {
  background: var(--linen);
  border-top: 1px solid var(--edge);
  border-bottom: 1px solid var(--edge);
}
/* A band of numbers needs less air around it than a band of prose. */
.mkt-band--tight {
  padding: 72px 32px;
}
.mkt-band--navy {
  background: var(--mug-navy);
  color: var(--paper);
}
.mkt__inner {
  max-width: 1180px;
  margin: 0 auto;
}
.mkt-measure {
  max-width: 65ch;
}
/* /printables/ is the only marketing page with an h1 in the .mkt shell. */
.mkt h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 60px;
  line-height: 68px;
  letter-spacing: 0;
  color: var(--ink);
  text-wrap: balance;
  margin: 0 0 24px;
}
.mkt h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 40px;
  line-height: 48px;
  letter-spacing: 0;
  color: var(--ink);
  text-wrap: balance;
  margin: 0 0 24px;
}
/* Accent word inside a display heading — italic, red, one per heading. */
.mkt h1 em,
.mkt h2 em {
  font-style: italic;
  color: var(--red);
}
.mkt h3 {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 20px;
  line-height: 28px;
  color: var(--ink);
  margin: 0 0 8px;
}
.mkt p {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 18px;
  line-height: 30px;
  color: var(--ink-body);
  margin: 0 0 16px;
}
.mkt p:last-child {
  margin-bottom: 0;
}
/* `.mkt p` (0-1-1) outranks a bare `.eyebrow` (0-1-0), so without this the
   /printables/ eyebrow renders as 18px body text with mono tracking on it. */
.mkt p.eyebrow {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  color: var(--red);
  margin: 0 0 8px;
}
.mkt p a {
  color: var(--red);
  text-decoration: underline;
  text-underline-offset: 5px;
  text-decoration-thickness: 1px;
}
.mkt p a:hover,
.mkt p a:focus-visible {
  color: var(--red-hover);
  text-decoration-thickness: 2px;
}
/* 0-2-1, so the 32px sticks: `.mkt p` used to swallow it (see the block
   comment above). */
.mkt p.mkt-intro {
  max-width: 65ch;
  margin: 0 0 32px;
}

/* Section 1: the three steps */
.mkt-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}
.mkt-step {
  background: var(--linen);
  border: 1px solid var(--edge);
  border-radius: var(--radius-card);
  padding: 32px;
}
.mkt .mkt-step p {
  font-size: 16px;
  line-height: 28px;
  color: var(--ink-soft);
  margin: 0;
  text-wrap: pretty;
}
.mkt p.mkt-step__num {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  color: var(--red);
  margin: 0 0 16px;
}

/* Section 2: the theme screenshots */
.mkt-themes {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  justify-items: center;
  gap: 32px;
}
.mkt-theme {
  max-width: 300px;
  margin: 0;
}
.mkt-theme img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-figure);
  box-shadow: var(--shadow-lift);
}
.mkt-theme figcaption {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
  text-align: center;
  margin-top: 16px;
}

/* Printables: preview cards extend .mkt-theme with a description + actions.
   The whole figure becomes the card, so the caption goes back to sentence-case
   UI type — a mono uppercase paragraph would be unreadable at this length. */
/* Four printables in a three-column grid leaves one card orphaned on its own
   row. They are the page's whole point, so they get a row of four. */
.mkt-themes:has(.printable) {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.printable {
  max-width: none;
  background: var(--paper);
  border: 1px solid var(--edge);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-soft);
  padding: 16px 16px 24px;
}
.printable img {
  border: 1px solid var(--edge);
  border-radius: 8px;
  box-shadow: none;
}
.printable figcaption {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 18px;
  line-height: 28px;
  letter-spacing: 0;
  text-transform: none;
  text-align: left;
  color: var(--ink);
  margin-top: 16px;
}
.printable__desc {
  display: block;
  font-weight: 400;
  font-size: 16px;
  line-height: 24px;
  color: var(--ink-soft);
  margin-top: 4px;
}
.printable__actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin: 20px 0 0;
}
/* Outranks the .mkt p a link treatment, which would repaint the pill's label */
.mkt .printable__actions a.cta-button {
  color: var(--paper);
  text-decoration: none;
  padding: 12px 24px;
  min-height: 44px;
  font-size: 16px;
  line-height: 24px;
  white-space: nowrap;
}
.mkt .printable__actions .printable__view {
  font-size: 16px;
  line-height: 24px;
  white-space: nowrap;
}

/* Section 3: text beside the photo */
.mkt-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 48px;
  align-items: center;
}
.mkt-split .img-placeholder {
  margin: 0;
}

/* Section 4: questions. A narrower column than .mkt-measure — a run of
   question-and-answer pairs reads as a document, not as a marketing band. */
.mkt-faq {
  max-width: 760px;
}
.mkt-faq h3 {
  margin: 32px 0 8px;
}

/* Section 5: the closing band */
.mkt-close {
  text-align: center;
}
.mkt-close__icon {
  display: block;
  width: 88px;
  height: 88px;
  border-radius: 20px;
  margin: 0 auto 24px;
}
.mkt-band--navy h2 {
  color: var(--paper);
  margin-bottom: 16px;
}
.mkt .mkt-close h2 {
  font-size: 48px;
  line-height: 56px;
}
.mkt-band--navy p {
  color: var(--mug-band-ink);
}
.mkt-close__cta {
  margin: 32px 0 0;
}

@media (max-width: 959px) {
  .mkt-steps,
  .mkt-split {
    grid-template-columns: minmax(0, 1fr);
  }
  .mkt-split {
    gap: 32px;
  }
  /* Themes become a swipeable row with the next board peeking in */
  .mkt-themes {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding: 0 20px;
    gap: 16px;
    padding-bottom: 8px;
  }
  .mkt-theme {
    flex: 0 0 72%;
    scroll-snap-align: center;
  }
  .mug-hero h1 {
    font-size: 56px;
    line-height: 64px;
  }
}
@media (max-width: 767px) {
  .mkt-band {
    padding: 96px 20px;
  }
  .mkt-band--tight {
    padding: 72px 20px;
  }
  .mug-hero {
    padding: 88px 20px 0;
  }
  .mkt h1 {
    font-size: 44px;
    line-height: 52px;
  }
  .mug-hero h1 {
    font-size: 44px;
    line-height: 52px;
  }
}
@media (max-width: 479px) {
  .mug-hero h1 {
    font-size: 36px;
    line-height: 44px;
  }
  .mkt h1 {
    font-size: 36px;
    line-height: 44px;
  }
  .mkt h2 {
    font-size: 28px;
    line-height: 36px;
  }
  .mkt .mkt-close h2 {
    font-size: 36px;
    line-height: 44px;
  }
  .mkt-band {
    padding: 64px 20px;
  }
  .mkt-band--tight {
    padding: 56px 20px;
  }
  .mug-hero {
    padding-top: 64px;
  }
}

/* ---------- Nobs landing (page-scoped: .nobs-hero, .nobs-points, .nobs-facts,
   .nobs-note) ----------
   The felt band mirrors .mug-hero. Sections 2-4 live inside <main class="mkt">
   and inherit the shared marketing type, so this block carries only what the
   owner's design changes: the band, two type-only grids, and the margins that
   differ from .mkt's defaults (W-0031). */

/* Section 1: the felt hero band */
.nobs-hero {
  background: linear-gradient(var(--felt-top), var(--felt-bottom));
  color: var(--nobs-ink);
  padding: 88px 32px 0;
  text-align: center;
}
.nobs-hero__inner {
  max-width: 1180px;
  margin: 0 auto;
}
.nobs-hero .eyebrow {
  color: var(--nobs-accent);
  margin: 0 0 20px;
}
.nobs-hero h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 76px;
  line-height: 80px;
  letter-spacing: -0.01em;
  color: var(--nobs-ink);
  text-wrap: balance;
  margin: 0 0 20px;
}
/* The one accent word, italic in the app's accent blue. */
.nobs-hero h1 em {
  font-style: italic;
  color: var(--nobs-accent);
}
.nobs-hero__lead {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 20px;
  line-height: 32px;
  color: var(--nobs-lead);
  max-width: 44ch;
  margin: 0 auto;
  text-wrap: pretty;
}
/* The beta download block (W-0060). Two mutually exclusive affordances ship in
   the markup and CSS picks one, so it is no-JS safe and needs no UA sniffing:
   a scannable QR where the reader cannot install the app on the device in
   front of them, Apple's badge where they can. The boundary is 960, the site's
   existing "desktop affordance" line (W-0057 §6, the article TOC rail) — a
   tablet gets the tappable badge, not a code it would have to scan with
   itself. */
.nobs-get {
  margin: 40px 0 0;
}
/* The extra bottom padding is load-bearing, not styling. A QR needs a 4-module
   quiet zone to scan reliably: at this render the code carries ~8.7px of its
   own and wants ~13.7px, so the tag must stay ~5px clear of the last module.
   The 20px bottom shelf leaves 8px once the tag's 12px overhang is subtracted
   (16.7px of quiet zone total). Re-measure if the QR size or the tag changes —
   don't eyeball it. Radius is deliberately off the --radius-card scale: at this
   size 16px reads square, so the card carries its own rounder corner. */
.nobs-get__card {
  position: relative;
  display: inline-block;
  padding: 12px 12px 20px;
  background: var(--paper);
  border-radius: 28px;
  box-shadow: var(--shadow-onfelt);
  /* Clears the tag's 12px overhang below the card. */
  margin: 0 0 12px;
}
.nobs-get__qr {
  display: block;
  width: 144px;
  height: 144px;
}
/* Floats on the card's bottom edge, half in and half out. */
.nobs-get__tag {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translate(-50%, 50%);
  white-space: nowrap;
  margin: 0;
  padding: 4px 12px;
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 14px;
  line-height: 16px;
  color: var(--paper);
  background: var(--red);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-cta);
}
.nobs-get__badge {
  display: none;
  text-decoration: none;
}
/* Height is the authored dimension and stays on the 4pt scale; width is left to
   the SVG's own 119.66:40 ratio so Apple's lockup is never reproportioned
   (~179.5px). Apple also asks for clear space of at least 10% of the badge
   height — the 20px note margin below and the hero's own spacing above cover
   the 6px that needs. */
.nobs-get__badge img {
  display: block;
  height: 60px;
  width: auto;
}
.nobs-get__note {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 14px;
  line-height: 20px;
  color: var(--nobs-lead);
  margin: 20px 0 0;
}

/* Sits on the bottom edge of the band, no gap below it. The art carries its own
   glow in the alpha channel — deliberately no border, radius or box-shadow. */
.nobs-hero__art {
  display: block;
  width: 100%;
  max-width: 980px;
  height: auto;
  margin: 56px auto 0;
}

/* Section 2: three things worth knowing. Type only — no cards, no borders, no
   fill. That is what separates it from the Muggins landing's .mkt-steps. */
.mkt .nobs-points h2 {
  text-align: center;
  margin-bottom: 64px;
}
.nobs-points__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 56px;
}
.mkt .nobs-points__grid p {
  font-size: 16px;
  line-height: 28px;
  color: var(--ink-soft);
  margin: 0;
  text-wrap: pretty;
}
.mkt p.nobs-points__num {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  color: var(--red);
  margin: 0 0 12px;
}

/* Section 3: the facts row. No h2 — the row speaks for itself. */
.nobs-facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  text-align: center;
}
.mkt .nobs-facts p {
  margin: 0;
}
.mkt .nobs-facts__value {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 56px;
  line-height: 60px;
  letter-spacing: 0;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin: 0 0 8px;
}
.mkt .nobs-facts__label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Section 4: the Muggins cross-link, which carries the page's one CTA. The
   button is a bare <a>, never wrapped in a <p> — `.mkt p a` (0-1-2) would beat
   `.cta-button`'s colour (0-1-0) and paint the label red on a red pill. */
.nobs-note {
  text-align: center;
}
.mkt .nobs-note h2 {
  margin-bottom: 16px;
}
.mkt .nobs-note__body {
  max-width: 52ch;
  margin: 0 auto 32px;
}

/* The points columns stack early: each holds a paragraph, unreadable narrow.
   The facts row holds its shape much further down — three numbers read as one
   comparison, and stacking that at tablet width destroys the only reason the
   row exists. Its labels have room to wrap to two lines long before then. */
@media (max-width: 959px) {
  .nobs-points__grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .nobs-hero h1 {
    font-size: 56px;
    line-height: 64px;
  }
  /* Below the desktop line the reader is holding the device that installs it:
     swap the scannable code for the tappable badge. */
  .nobs-get__card {
    display: none;
  }
  .nobs-get__badge {
    display: inline-block;
  }
}
@media (max-width: 767px) {
  .nobs-hero {
    padding: 88px 20px 0;
  }
  .nobs-hero h1 {
    font-size: 44px;
    line-height: 52px;
  }
}
@media (max-width: 560px) {
  .nobs-facts {
    grid-template-columns: minmax(0, 1fr);
    gap: 32px;
  }
}
@media (max-width: 479px) {
  .nobs-hero h1 {
    font-size: 36px;
    line-height: 44px;
  }
  .nobs-hero {
    padding-top: 64px;
  }
}

/* ---------- Hand calculator (page-scoped: .hc-page, .hc*) ----------
   The site's first interactive component (W-0047), restyled to Linen Editorial
   in WP-6. Markup is src/_includes/hand-calculator.njk; behaviour is
   /assets/js/hand-scorer.js, which is untouched — every class and attribute the
   script reads or writes is styled here, including the states it toggles
   (--filled, --next, --hi, --pending, --scored) and the rows it builds at
   runtime (.hc__chip, .hc__line*).

   Two deliberate departures from the handoff frame:
   - Rank glyphs are Hanken Grotesk 600, not Instrument Serif. The mock sets card
     numbers in the serif; the owner rejected it for glyphs that have to read at a
     glance mid-game.
   - Disabled keys are not dimmed with opacity. The mock adds `opacity: 0.6` on
     top of the linen fill, which drops --ink-muted below AA. The flattened fill,
     --rule border and muted text carry the state on their own, and a disabled key
     here is a normal outcome (that card is already on the table), never an error.

   Sizes are on the 4pt grid (DESIGN.md §Scale) with 14 and 18 as the sanctioned
   exceptions. Shadows, tracking and the pill radius are grid-exempt. */
.hc {
  --hc-key-shadow: 0 1px 2px rgba(26, 24, 21, 0.05);
  --hc-key-shadow-pending: 0 4px 12px rgba(26, 24, 21, 0.18);
  --hc-card-shadow: 0 2px 8px rgba(26, 24, 21, 0.05);
  --hc-card-shadow-cut: 0 2px 8px rgba(196, 56, 46, 0.1);
  --hc-card-shadow-hi: 0 0 0 4px rgba(196, 56, 46, 0.14), 0 2px 8px rgba(196, 56, 46, 0.2);
  --hc-cta-shadow: 0 6px 18px rgba(196, 56, 46, 0.22);
  --hc-radius-shell: 20px;
  --hc-radius-tray: 16px;
  --hc-radius-key: 12px;
  --hc-radius-row: 8px;
  margin: 32px 0 64px;
}
.hc__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The tool ships inert and the script enables it, so a reader without JavaScript
   gets an explanation instead of a keypad that swallows taps. */
.hc__nojs {
  font-family: var(--font-ui);
  font-size: 18px;
  line-height: 28px;
  color: var(--ink-body);
  background: var(--linen);
  border: 1px solid var(--edge);
  border-radius: var(--radius-card);
  padding: 16px 20px;
  margin: 0 0 20px;
  max-width: 62ch;
}
.hc[data-hc-ready] .hc__nojs {
  display: none;
}

.hc__grid {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  align-items: flex-start;
}
/* Both columns carry a flex-basis floor, so the tool folds to one column on its
   own at around 900px. The breakpoint below is only for the card chrome. */
.hc__input {
  flex: 1.35 1 480px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

/* Hand / crib toggle. Real radios in a fieldset: arrow keys, focus and grouping
   all come from the platform, which is the W-0023 precedent. The tray is a linen
   pill; the selected label is the ink pill inside it. */
.hc__mode {
  align-self: flex-start;
  display: inline-flex;
  gap: 4px;
  margin: 0;
  padding: 4px;
  background: var(--linen);
  border: 1px solid var(--edge);
  border-radius: var(--radius-pill);
}
.hc__mode-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
}
.hc__mode-label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 12px 24px;
  border-radius: var(--radius-pill);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  cursor: pointer;
}
.hc__mode-input:checked + .hc__mode-label {
  background: var(--ink);
  color: var(--paper);
}
.hc__mode-input:focus-visible + .hc__mode-label {
  outline: 3px solid var(--red);
  outline-offset: 2px;
}
.hc__mode-input:disabled + .hc__mode-label {
  opacity: 0.5;
  cursor: default;
}

/* Card row: four cards, a plus, the cut. */
.hc__cards {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr)) 20px minmax(0, 1fr);
  gap: 8px 8px;
  align-items: end;
}
.hc__cards-label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.hc__cards-label--hand {
  grid-column: 1 / 6;
  color: var(--ink-muted);
}
.hc__cards-label--cut {
  grid-column: 6 / 7;
  color: var(--red);
}
.hc__plus {
  grid-column: 5 / 6;
  justify-self: center;
  align-self: center;
  padding-bottom: 24px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 16px;
  color: var(--ink-muted);
}
.hc__card {
  aspect-ratio: 5 / 7;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 0;
  background: var(--linen);
  /* Border width is held at 2px across every state so nothing shifts when a
     card fills or gets highlighted. Empty slots are dashed, filled ones solid. */
  border: 2px dashed var(--edge);
  border-radius: var(--radius-figure);
  cursor: pointer;
}
.hc__card--next {
  border-color: var(--red);
}
.hc__card--filled {
  background: var(--paper);
  border-style: solid;
  box-shadow: var(--hc-card-shadow);
}
/* The cut is the one card that is not yours, so it keeps the red edge it had as
   the next slot and fills a shade warmer than the hand. */
.hc__card--cut.hc__card--filled {
  background: var(--linen-bright);
  border-color: var(--red);
  box-shadow: var(--hc-card-shadow-cut);
}
/* Selecting a breakdown line outlines the cards that earned it. The halo is what
   separates a highlighted cut card from a merely filled one — the cut already
   wears the red border — so this needs to outrank the two-class cut rule above. */
.hc__card.hc__card--hi {
  border-style: solid;
  border-color: var(--red);
  box-shadow: var(--hc-card-shadow-hi);
}
.hc__card:disabled {
  cursor: default;
}
.hc__card-rank {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 24px;
  line-height: 28px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.hc__card:not(.hc__card--filled) .hc__pip {
  display: none;
}
.hc__card .hc__pip {
  width: 20px;
  height: 20px;
}

/* Suit pips. The artwork is one currentColor sprite (src/_includes/suit-sprite.njk);
   the diamond is a narrow lozenge in a square box, so it is nudged to match the
   optical weight of the other three. */
.hc__pip {
  display: block;
  color: var(--ink);
}
.hc__pip[data-suit='h'],
.hc__pip[data-suit='d'] {
  color: var(--red);
}
.hc__pip[data-suit='d'] {
  transform: scale(1.06);
}

/* Keypad. Full-bleed tinted band on a phone, rounded linen tray once there is room. */
.hc__pad {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin: 0 -20px;
  padding: 16px 20px;
  background: var(--linen);
  border-top: 1px solid var(--edge);
  border-bottom: 1px solid var(--edge);
}
.hc__pad-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.hc__pad-label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
/* The suit label becomes "5 of …" while a rank waits for its suit. */
.hc__pad-label--pending {
  color: var(--red);
}
.hc__ranks {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.hc__suits {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}
.hc__key {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  font-family: var(--font-ui);
  font-weight: 600;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--edge);
  border-radius: var(--hc-radius-key);
  box-shadow: var(--hc-key-shadow);
  cursor: pointer;
}
.hc__key--rank {
  /* five to a row, three rows */
  flex: 0 0 calc((100% - 32px) / 5);
  min-height: 52px;
  font-size: 18px;
  line-height: 24px;
}
.hc__key--suit {
  min-height: 60px;
}
.hc__key--suit .hc__pip {
  width: 24px;
  height: 24px;
}
.hc__key:hover:not(:disabled) {
  border-color: var(--ink);
}
/* A rank waiting for its suit. The filled pill is the only place in the tool that
   says "the next tap goes here", so it stays loud. */
.hc__key--rank[aria-pressed='true'] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
  box-shadow: var(--hc-key-shadow-pending);
}
/* Real `disabled`, and never styled as an error: the card is simply already on
   the table, or the hand is full. Flat linen, no shadow, no dimming. */
.hc__key:disabled {
  background: var(--linen);
  border-color: var(--rule);
  color: var(--ink-muted);
  box-shadow: none;
  cursor: default;
}
.hc__key:active:not(:disabled) {
  transform: translateY(1px);
  box-shadow: none;
}

.hc__actions {
  display: flex;
  gap: 12px;
  align-items: stretch;
}
.hc__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 56px;
  padding: 8px 24px;
  border-radius: var(--radius-pill);
  cursor: pointer;
}
.hc__btn--ghost {
  flex: 0 0 112px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  background: var(--paper);
  border: 1px solid var(--edge);
}
.hc__btn--ghost:hover:not(:disabled) {
  border-color: var(--ink);
  color: var(--ink);
}
.hc__btn--primary {
  flex: 1 1 auto;
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 18px;
  line-height: 24px;
  color: var(--paper);
  background: var(--red);
  border: 1px solid var(--red);
  box-shadow: var(--hc-cta-shadow);
}
.hc__btn--primary:hover:not(:disabled),
.hc__btn--primary:focus-visible:not(:disabled) {
  background: var(--red-hover);
  border-color: var(--red-hover);
}
/* Score is disabled until five cards are in, and its label says how many are
   still missing, so the flat state is information rather than a dead end. */
.hc__btn--primary:disabled {
  color: var(--ink-muted);
  background: var(--linen);
  border-color: var(--edge);
  box-shadow: none;
  cursor: default;
}
.hc__btn--ghost:disabled {
  background: var(--linen);
  border-color: var(--rule);
  cursor: default;
}
.hc__btn:active:not(:disabled) {
  transform: translateY(1px);
}

/* Results panel: on screen at every width, filled in place when Score is tapped. */
.hc__result {
  flex: 1 1 380px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 24px;
  background: var(--linen);
  border: 1px solid var(--edge);
  border-radius: var(--hc-radius-tray);
}
.hc__result-label {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
/* Before a score there is no number to show, and a dimmed placeholder numeral
   reads as a loading skeleton, so the row collapses and the hint carries the
   panel instead. The live region inside it stays in the DOM either way. */
.hc__total {
  display: none;
  align-items: baseline;
  gap: 16px;
}
.hc__result--scored .hc__total {
  display: flex;
}
.hc__total-num {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 72px;
  line-height: 72px;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.hc__total-unit {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.hc__lines {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  max-width: none;
}
.hc__lines li {
  margin: 0;
}
.hc__line {
  display: flex;
  align-items: baseline;
  gap: 12px;
  width: 100%;
  padding: 12px;
  text-align: left;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: var(--hc-radius-row);
  font-family: var(--font-ui);
  cursor: pointer;
}
.hc__lines li:last-child .hc__line {
  border-bottom: 0;
}
/* Hover and focus both outline the cards, so both lift the row off the linen.
   The selected row keeps the lift after the pointer leaves, and the hairline ring
   is what tells it apart from whatever is merely under the cursor. */
.hc__line:hover,
.hc__line--hi {
  background: var(--paper);
}
.hc__line--hi {
  box-shadow:
    0 0 0 1px var(--edge),
    var(--hc-key-shadow);
}
.hc__line-label {
  font-weight: 600;
  font-size: 16px;
  line-height: 24px;
  color: var(--ink);
  white-space: nowrap;
}
.hc__line-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 20px;
  color: var(--ink-muted);
}
.hc__chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.hc__chip .hc__pip {
  width: 12px;
  height: 12px;
}
.hc__line-pts {
  margin-left: auto;
  font-weight: 600;
  font-size: 18px;
  line-height: 24px;
  font-variant-numeric: tabular-nums;
  color: var(--red);
}
.hc__note {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  line-height: 16px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.hc__hint {
  margin: 0;
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 24px;
  color: var(--ink-muted);
}
/* One hairline closes the panel, drawn by whichever of the two footer lines comes
   first — the 29 note only exists for one hand in 216,580. Before a score there is
   nothing above the hint to rule off. */
.hc__result--scored .hc__note,
.hc__result--scored .hc__hint {
  border-top: 1px solid var(--edge);
  padding-top: 16px;
}
.hc__note:not([hidden]) + .hc__hint {
  border-top: 0;
  padding-top: 0;
}

/* Page shell: intro column, then the full-width tool, then article plus rail. */
.hc-page__below {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  gap: 48px 72px;
  align-items: start;
}
@media (max-width: 959px) {
  .hc-page__below {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (min-width: 768px) {
  .hc {
    margin: 40px 0 80px;
    padding: 40px;
    background: var(--paper);
    border: 1px solid var(--edge);
    border-radius: var(--hc-radius-shell);
  }
  .hc__grid {
    gap: 40px;
  }
  .hc__pad {
    margin: 0;
    padding: 20px;
    border: 1px solid var(--edge);
    border-radius: var(--hc-radius-tray);
  }
  .hc__card-rank {
    font-size: 28px;
    line-height: 32px;
  }
  .hc__result {
    padding: 32px;
  }
  .hc__total-num {
    font-size: 104px;
    line-height: 104px;
  }
}

/* ---------- Focus & motion ---------- */
:focus-visible {
  outline: 3px solid var(--red);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
  /* One exemption. The reading-progress fill is not motion — it is a readout of
     where you are in the article, and the blanket `animation: none` above would
     freeze it at zero for the people most likely to want it. The transitions
     stay dead, so the header swap is instant, which is the point. */
  @supports (animation-timeline: --a) and (timeline-scope: --a) {
    .read-progress__fill {
      animation: read-progress-grow auto linear both !important;
      animation-timeline: --reading-article !important;
      animation-range: contain 0% contain 100% !important;
    }
  }
}
