/* =========================================================================
   Claude Code Fun — the Marketing site's one stylesheet.

   Hand-written, no build step, no preprocessor. Every page loads this file
   and nothing else. Tokens first, then the shell (header, navigation,
   footer), then the small number of shared primitives.

   Palette is taken from design/mockup-home.png. Every text token clears WCAG
   AA (4.5:1) against BOTH surfaces — the two ratios after each one below are
   on --surface and on --surface-sunk. The tightest pair in use is
   --ink-subtle on --surface-sunk at 4.9:1 (the footer's bottom bar).
   White on --brand is 6.7:1.
   ========================================================================= */

:root {
  /* ---- palette ------------------------------------------------------- */
  --brand: #4d30fb;          /* the one accent: links, marks, glyphs        */
  --brand-strong: #3a1ce0;   /* hover / pressed                             */
  --brand-tint: #eeebfe;     /* lavender surface behind brand text          */
  --brand-edge: #d9d3fd;     /* border on a brand tint                      */

  /* ---- the one pink ---------------------------------------------------- */
  /*
     Trans-flag pink, and it belongs to exactly one control: Get the Course.
     RETOOL_COPY's copy rules — "One direct CTA label on both sites: Get the
     Course. Brand pink is used on that control and nowhere else." Retool 7
     (#15) moved .course-link--buy onto it, which is every purchase pill on
     the site at once: the header, the hero, the Course card, the closing band
     and the footer. Nothing else on this page may take these three tokens.

     The label is near-black rather than white, because white on this pink is
     1.9:1 and unreadable. --pink-ink is 9.3:1 on the fill and 7.2:1 on the
     hover fill, so the control clears AA in both states.
  */
  --pink: #f5a9b8;           /* the fill                                    */
  --pink-strong: #e88ba0;    /* hover, and the resting edge                 */
  --pink-ink: #1a1a1a;       /* the label                     9.3:1 / 7.2:1 */

  --ink: #101018;            /* headings                18.9:1 / 17.7:1      */
  --ink-body: #2a2d3d;       /* body copy               13.6:1 / 12.8:1      */
  --ink-muted: #565b70;      /* secondary copy           6.7:1 /  6.3:1      */
  --ink-subtle: #686c82;     /* labels, meta             5.2:1 /  4.9:1      */

  --surface: #ffffff;
  --surface-sunk: #f7f7fd;   /* tinted band between sections               */
  --line: #e4e4f0;
  --line-strong: #d3d3e4;

  --focus: #1a0bb8;

  /* The only error colour on the site, and it draws exactly one thing: the
     border of a form control the visitor has typed into and left invalid
     (.field__control:user-invalid). 6.5:1 on --surface and 6.1:1 on
     --surface-sunk, so it clears AA as text too if anything ever needs to say
     something in it — and it is never the ONLY signal, because the browser's
     own validation message says what is wrong in words. */
  --danger: #b3261e;

  /* ---- type ---------------------------------------------------------- */
  --font-sans: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --text-xs: 0.75rem;    /* 12 */
  --text-sm: 0.875rem;   /* 14 */
  --text-base: 1rem;     /* 16 */
  --text-md: 1.0625rem;  /* 17 */
  --text-lg: 1.1875rem;  /* 19 */
  --text-xl: 1.5rem;     /* 24 */
  --text-2xl: 1.875rem;  /* 30 */
  --text-3xl: 2.375rem;  /* 38 */
  --text-4xl: 3.125rem;  /* 50 */

  --leading-tight: 1.12;
  --leading-snug: 1.35;
  --leading-normal: 1.62;

  --tracking-tight: -0.022em;
  --tracking-wide: 0.06em;

  /* ---- space (4px base) ---------------------------------------------- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 2.5rem;
  --space-8: 3rem;
  --space-9: 4rem;
  --space-10: 5rem;
  --space-11: 6.5rem;

  /* ---- shape --------------------------------------------------------- */
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 18px;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(16, 16, 24, 0.05);
  --shadow-md: 0 6px 24px rgba(16, 16, 24, 0.07);
  --shadow-brand: 0 8px 20px rgba(77, 48, 251, 0.24);
  /* The purchase pill's own lift. Cast in a deeper pink than the fill: the
     fill itself is light enough that a shadow tinted with it disappears. */
  --shadow-pink: 0 8px 20px rgba(196, 92, 117, 0.28);

  /* ---- layout -------------------------------------------------------- */
  --container: 1180px;
  --gutter: var(--space-5);
  --header-height: 72px;

  --transition: 150ms ease;
}

/* ---- reset ----------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-height) + var(--space-4));
}

body {
  background: var(--surface);
  color: var(--ink-body);
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
}

img,
svg {
  display: block;
  max-width: 100%;
  height: auto;
}

ul,
ol {
  list-style: none;
}

/* The Phase tabs hide their inactive panels with the `hidden` attribute, and
   a display rule elsewhere would quietly beat it. */
[hidden] {
  display: none !important;
}

a {
  color: var(--brand);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

h1,
h2,
h3 {
  color: var(--ink);
  font-weight: 800;
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
}

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

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

/* ---- layout primitives ----------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.skip-link {
  position: absolute;
  left: var(--space-4);
  top: var(--space-2);
  z-index: 100;
  transform: translateY(-200%);
  background: var(--ink);
  color: var(--surface);
  font-size: var(--text-sm);
  font-weight: 700;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  text-decoration: none;
}

.skip-link:focus {
  transform: translateY(0);
}

/* Reachable by a screen reader, invisible to everyone else. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- brand lockup ---------------------------------------------------- */

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: var(--ink);
}

/* The mark is Lucy — assets/brand/mascot.png, the transparent artwork, which
   is the canonical logo on the Course platform. It replaced a drawn "CC"
   monogram in a lavender rounded square.

   NO BACKGROUND, NO RADIUS, NO PADDING, and that is the whole point of using
   the transparent file: the artwork carries its own silhouette and must sit
   directly on whatever surface is behind it. The header is white and the
   footer is --surface-sunk, and Lucy reads on both. assets/brand/robot-icon.png
   is the same character on an opaque blue field and is NOT used here — it
   renders as a blue tile, which the Course platform hit and fixed.

   The source is 512x512 against a 38px box, so it is ~13x on a 1x display and
   ~6.7x on a 2x one. Crisp everywhere without a second asset to keep in sync.
   width/height are on the element too, so the lockup reserves its space before
   the image arrives and the brand name does not jump left. */
.brand__mark {
  width: 38px;
  height: 38px;
  flex: none;
  object-fit: contain;
}

.brand__name {
  font-size: var(--text-lg);
  font-weight: 800;
  letter-spacing: var(--tracking-tight);
  white-space: nowrap;
}

/* =========================================================================
   The two Course links.

   Get the Course is a purchase. Log in is a returning student. They are
   never the same control, on any breakpoint, in the header or the footer.
   scripts/check-links.mjs asserts that .course-link--buy and
   .course-link--return each carry a class the other does not, and that both
   have a rule here.
   ========================================================================= */

.course-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: inherit;
  font-size: var(--text-sm);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--transition), color var(--transition),
    border-color var(--transition), box-shadow var(--transition);
}

/* Purchase: a filled PINK pill. Loud on purpose, and the only pink control on
   the site — see the --pink tokens at the top of this file. It was a brand
   purple pill until Retool 7 (#15); changing it here rather than at each call
   site is what makes "brand pink is used on that control and nowhere else"
   true of the header, the hero, the Course card, the closing band and the
   footer in one edit, and keeps it true of anything that wears this class
   next. The edge is a shade deeper than the fill: on white, an unbordered
   #f5a9b8 pill loses its own silhouette. */
.course-link--buy {
  background: var(--pink);
  color: var(--pink-ink);
  font-weight: 700;
  padding: var(--space-3) var(--space-5);
  border: 1px solid var(--pink-strong);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-pink);
}

.course-link--buy:hover {
  background: var(--pink-strong);
  border-color: var(--pink-strong);
}

/* Return visit: a quiet text link with a key glyph. No fill, no pill, no
   border — a returning student should never mistake it for the sales pill. */
.course-link--return {
  background: none;
  color: var(--ink-muted);
  font-weight: 600;
  padding: var(--space-2) var(--space-1);
  border: 0;
  border-radius: 0;
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.32em;
}

.course-link--return:hover {
  color: var(--brand);
  text-decoration-color: currentColor;
}

.course-link--return::before {
  content: "";
  width: 15px;
  height: 15px;
  flex: none;
  background: currentColor;
  -webkit-mask: var(--icon-sign-in) center / contain no-repeat;
  mask: var(--icon-sign-in) center / contain no-repeat;
}

:root {
  /* An arrow going through a doorway: "you already have an account". */
  --icon-sign-in: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M15 12H3'/%3E%3C/svg%3E");
}

/* =========================================================================
   Header
   ========================================================================= */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--line);
}

.site-header__bar {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  min-height: var(--header-height);
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  margin-left: auto;
}

.site-nav__links {
  display: flex;
  align-items: center;
  gap: var(--space-6);
}

.site-nav__links a {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
  padding: var(--space-2) 0;
  transition: color var(--transition);
}

.site-nav__links a:hover,
.site-nav__links a[aria-current="page"] {
  color: var(--ink);
}

.site-nav__course {
  display: flex;
  align-items: center;
  gap: var(--space-5);
}

/* ---- the mobile menu button ------------------------------------------ */

.site-nav-toggle {
  display: none;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.site-nav-toggle__bars,
.site-nav-toggle__bars::before,
.site-nav-toggle__bars::after {
  display: block;
  width: 16px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}

.site-nav-toggle__bars {
  position: relative;
}

.site-nav-toggle__bars::before,
.site-nav-toggle__bars::after {
  content: "";
  position: absolute;
  left: 0;
}

.site-nav-toggle__bars::before {
  top: -5px;
}

.site-nav-toggle__bars::after {
  top: 5px;
}

/* =========================================================================
   Phone header.

   The navigation is stacked and visible by default, so a browser with
   JavaScript blocked still reaches every destination — including both
   Course links. The `js` class on <html> is what collapses it behind the
   Menu button; see js/site.js.
   ========================================================================= */

@media (max-width: 900px) {
  .site-header__bar {
    flex-wrap: wrap;
    padding-block: var(--space-3);
  }

  .site-nav-toggle {
    display: inline-flex;
  }

  .site-nav {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-4);
    width: 100%;
    margin-left: 0;
    padding-block: var(--space-4) var(--space-5);
    border-top: 1px solid var(--line);
  }

  .site-nav__links {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .site-nav__links a {
    display: block;
    font-size: var(--text-md);
    padding: var(--space-4) var(--space-1);
    border-bottom: 1px solid var(--line);
  }

  .site-nav__course {
    flex-direction: column-reverse; /* purchase first, then the quiet link */
    align-items: stretch;
    gap: var(--space-4);
    padding-top: var(--space-2);
  }

  .site-nav__course .course-link--buy {
    padding-block: var(--space-4);
    font-size: var(--text-md);
  }

  .site-nav__course .course-link--return {
    justify-content: flex-start;
    font-size: var(--text-md);
  }

  .js .site-nav {
    display: none;
  }

  .js .site-header[data-nav-open="true"] .site-nav {
    display: flex;
  }
}

/* =========================================================================
   Page content

   .page-intro is the plain heading block: eyebrow, h1, supporting line, no
   media beside it. Home outgrew it in issue #3 and now opens with .hero
   below; it stays here as the intro Guide and Showcase will use.
   ========================================================================= */

.page-intro {
  padding-block: var(--space-10) var(--space-9);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--brand-tint);
  color: var(--brand-strong);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-pill);
  margin-bottom: var(--space-5);
}

.page-intro h1 {
  font-size: clamp(2.375rem, 6vw, var(--text-4xl));
  max-width: 15ch;
  margin-bottom: var(--space-5);
}

.page-intro h1 .accent {
  color: var(--brand);
}

.page-intro__lede {
  color: var(--ink-muted);
  font-size: var(--text-lg);
  line-height: var(--leading-snug);
  max-width: 54ch;
}

/* =========================================================================
   Home's hero, with the Overview player.

   Two columns on a wide screen — copy left, player right — and one column on
   a phone, in that same order: a visitor reads what the site is before being
   offered a three-minute animation. Nothing is reordered by CSS, so the DOM
   order is the reading order at every width.

   The player is an iframe over project/master-cut.html, the coded animation
   this repo already serves. It rests on its looping end card and swaps to the
   full cut when the overlay over the frame is used. ("Watch Overview" used to
   do it too; Retool 7 retired that button — see the actions below.)
   ========================================================================= */

.hero {
  padding-block: var(--space-10) var(--space-9);
}

/* The copy column went from the narrower of the two to the wider one in Retool
   7. It used to hold a six-word lockup and a one-sentence lede; it now holds
   two sentences of headline and four of subhead, and the player is the same
   16:9 box it always was. */
.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--space-8) var(--space-9);
}

/* ---- the copy column -------------------------------------------------- */

/* SMALLER THAN IT WAS, because the headline is a different kind of sentence.
   It used to be "From Idea to Impact / with Claude Code" — six short words in
   a lockup, which is what 50px is for. Retool 7's headline is two full
   sentences (RETOOL_COPY §A1) and at 50px in this column it sets to nine
   lines. Topping out at --text-3xl keeps it to five and leaves it the largest
   type on the page, which is what an <h1> is owed. */
.hero__title {
  font-size: clamp(1.75rem, 3.4vw, var(--text-3xl));
  margin-bottom: var(--space-5);
}

/* Two lines by design, but they are spans and not <br>: at narrow widths each
   one wraps on its own instead of being forced into an awkward break. */
.hero__title-line {
  display: block;
}

.hero__title .accent {
  color: var(--brand);
}

.hero__lede {
  color: var(--ink-muted);
  font-size: var(--text-lg);
  line-height: var(--leading-snug);
  max-width: 46ch;
  margin-bottom: var(--space-7);
}

/* ---- the one button, and the way past it ------------------------------- */
/*
   TWO PILLS USED TO SIT HERE — .hero__action--primary ("Explore the
   Framework") and .hero__action--secondary ("Watch Overview"). Retool 7 (#15)
   retired both, so their rules went with them rather than being left behind as
   selectors nothing matches. What replaced them is one purchase pill and one
   text link, which is the arrangement RETOOL_COPY §A1 specifies and the same
   arrangement the closing band already uses at the other end of the page.

   The Overview did not lose its play control: the overlay on the frame itself
   is a real link to the full cut, works with JavaScript blocked, and is what
   scripts/check-overview.mjs now checks in the retired button's place.
*/

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-6);
}

/* A size up from the header's pill, and the same size as the closing band's.
   Same class, same label, same destination — this is simply the first one a
   visitor is offered and the band at the bottom is the last. */
.hero__cta {
  font-size: var(--text-base);
  padding: var(--space-4) var(--space-6);
}

/* The transitional call to action, and a text link because RETOOL_COPY forbids
   a second button beside the pink one. Underlined at rest, in the same hairline
   the returning-student link uses, so it reads as a link before it is hovered.

   NO ::after ARROW. The arrow is a character in the copy ("… free →"), and
   drawing a second one would put two arrows a few characters apart — which is
   what scripts/verify-copy.mjs reads as somebody stating the Phase chain. */
.hero__alt {
  display: inline-flex;
  align-items: center;
  color: var(--ink-body);
  font-size: var(--text-base);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.32em;
  transition: color var(--transition), text-decoration-color var(--transition);
}

.hero__alt:hover {
  color: var(--brand-strong);
  text-decoration-color: currentColor;
}

:root {
  --icon-arrow-right: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 12' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 6h17M13 1l5 5-5 5'/%3E%3C/svg%3E");
}

/* ---- the player ------------------------------------------------------- */

.hero__player {
  padding: var(--space-4);
  background: linear-gradient(158deg, var(--brand-tint), var(--surface-sunk) 62%, var(--surface));
  border: 1px solid var(--brand-edge);
  border-radius: 26px;
  box-shadow: var(--shadow-md);
}

.hero__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}

/* Older engines without aspect-ratio still get a 16:9 box. */
@supports not (aspect-ratio: 1) {
  .hero__frame {
    height: 0;
    padding-top: 56.25%;
  }
}

/* One rule for both states of the player: the poster Home rests on, and the
   iframe js/site.js builds in its place when somebody presses play. They are
   the same box, so the swap moves nothing. */
.hero__frame__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  border: 0;
  object-fit: cover;
}

/* `display: block` above outranks the browser's own [hidden] rule, so the
   poster would stay visible under the animation without this. */
.hero__frame__media[hidden] {
  display: none;
}

/* The overlay is a real link to the full cut, so the Overview is still
   reachable with JavaScript blocked; js/site.js intercepts the click and
   swaps the frame's source instead of navigating. */
.hero__frame__play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--transition);
}

.hero__frame__play:hover {
  background: rgba(77, 48, 251, 0.07);
}

/* .hero__frame clips its overflow, so the shared outward focus ring would be
   cut off on an overlay pinned to its edges. Draw this one inside. */
.hero__frame__play:focus-visible {
  outline-offset: -5px;
}

.hero__player[data-overview-state="playing"] .hero__frame__play {
  display: none;
}

.hero__frame__play-mark {
  display: grid;
  place-items: center;
  width: 76px;
  height: 76px;
  background: var(--brand);
  color: #fff;
  border-radius: 50%;
  box-shadow: var(--shadow-brand), 0 0 0 6px rgba(255, 255, 255, 0.62);
  transition: transform var(--transition), background var(--transition);
}

.hero__frame__play:hover .hero__frame__play-mark {
  background: var(--brand-strong);
  transform: scale(1.06);
}

.hero__frame__play-mark svg {
  width: 30px;
  height: 30px;
  margin-left: 4px;
}

/* --ink-muted, not --ink-subtle: on the panel's lavender end --ink-subtle is
   4.4:1 and misses AA. This pair is 5.7:1. */
.hero__frame__caption {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  padding: var(--space-4) var(--space-2) var(--space-1);
}

.hero__frame__caption strong {
  color: var(--ink);
  font-weight: 700;
}

/* ---- narrow screens ---------------------------------------------------- */

@media (max-width: 900px) {
  .hero {
    padding-block: var(--space-8) var(--space-8);
  }

  .hero__grid {
    grid-template-columns: 1fr;
    gap: var(--space-7);
  }

  .hero__lede {
    margin-bottom: var(--space-6);
  }

  .hero__player {
    padding: var(--space-3);
    border-radius: 20px;
  }

  .hero__frame__play-mark {
    width: 62px;
    height: 62px;
  }

  .hero__frame__play-mark svg {
    width: 24px;
    height: 24px;
  }
}

@media (max-width: 560px) {
  .hero__actions {
    flex-direction: column;
    align-items: stretch;
  }

  /* The pill goes full width; the text link stays a text link and centres
     under it rather than stretching into something button-shaped. */
  .hero__cta {
    width: 100%;
  }

  .hero__alt {
    justify-content: center;
  }
}

/* =========================================================================
   The stakes — what staying in the chat window costs

   Four costs, and the styling has to keep saying "cost" after the reader has
   stopped reading the words. So: no icon squares, no tick marks, no brand
   tint. Those belong to the benefit cards and the "what you'll learn" list
   further down, and a stakes band wearing them would read as four more things
   the site is offering. What it wears instead is a rule over each item — a
   ledger, and nothing else.

   ON --surface, IMMEDIATELY UNDER THE WHITE HERO, which is the same
   arrangement .rail and .benefits already use: two white bands separated by a
   section head rather than by a tint. A tint here would meet .course's tint
   one hairline later and the two would read as a single grey run.

   TWO COLUMNS, NOT FOUR. The items are 8 to 40 words and a four-across row
   would set the short one as a single line beside three paragraphs.

   Contrast on --surface: --ink 18.9:1, --ink-body 13.6:1.
   ========================================================================= */

.stakes {
  padding-block: var(--space-10) var(--space-9);
}

.stakes__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: var(--space-6) var(--space-9);
  max-width: 62rem;
  margin-inline: auto;
}

.stakes__item {
  color: var(--ink-body);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  padding-top: var(--space-4);
  border-top: 2px solid var(--line-strong);
}

@media (max-width: 760px) {
  .stakes {
    padding-block: var(--space-9) var(--space-8);
  }

  .stakes__list {
    grid-template-columns: 1fr;
    gap: var(--space-5);
  }
}

/* =========================================================================
   The Course.

   The mockup's first tinted band, and still the first on Home — the hero and
   the stakes band above it are both white, so the tint alone says "new
   subject" before a word is read.
   The mockup has no Course section, so this is built in its language rather
   than copied from it: white card on the tint, hairline border, generous
   radius, one brand accent, nothing new invented.

   Three parts on a grid: the promise across the top, the offer card down the
   right, and what you'll learn filling the left. The card is second in the DOM
   and not third, so a phone reads promise → card → detail; the card is the
   point of the section and must not be at the bottom of it.

   THE CARD USED TO OPEN ON A PRICE. It now opens on the Course — the Sales
   page's own eyebrow, headline and three feature rows (issue #13). The paid
   Anthropic plan box that sat between the rows and the button came out too, on
   the owner's instruction in the follow-up. Two things follow. The price type
   scale is gone, and the card is a good deal taller than the one it replaced,
   which is why it is no longer sticky: see the note on that rule below.

   Contrast, on this section's two surfaces (--surface-sunk and --brand-tint):
   --ink 17.7:1 / 16.2:1, --ink-body 12.8:1 / 11.7:1, --ink-muted 6.3:1 /
   5.7:1. --ink-subtle is 4.4:1 on --brand-tint and is not used there.
   ========================================================================= */

.course {
  background: var(--surface-sunk);
  border-block: 1px solid var(--line);
  padding-block: var(--space-10);
}

/* TWO PARTS SINCE RETOOL 8 (#16), NOT THREE. "What you'll learn to do" used to
   fill a `learn` area under the promise; RETOOL_COPY Part A puts it at position
   6 as its own band, so it left this grid and took its row with it. The row had
   to go rather than be left empty: a named area with nothing in it still costs
   a row gap, which is 3rem of unexplained white between the promise and the
   bottom of the card. */
.course__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  grid-template-areas: "intro offer";
  align-items: start;
  gap: var(--space-8) var(--space-9);
}

.course__intro {
  grid-area: intro;
}

/* ---- the promise ------------------------------------------------------ */

.course__title {
  font-size: clamp(1.875rem, 4.2vw, var(--text-3xl));
  margin-bottom: var(--space-4);
}

.course__lede {
  color: var(--ink-body);
  font-size: var(--text-lg);
  line-height: var(--leading-snug);
  max-width: 44ch;
  margin-bottom: var(--space-4);
}

/* The one line that separates this from everything else on the site. It is a
   sentence, not a heading, so it is weighted rather than sized up. */
.course__plain {
  color: var(--ink);
  font-size: var(--text-md);
  font-weight: 700;
}

/* ---- the offer --------------------------------------------------------- */

.course__offer {
  grid-area: offer;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-5);
  background: var(--surface);
  border: 1px solid var(--brand-edge);
  border-radius: 22px;
  box-shadow: var(--shadow-md);
  padding: var(--space-6);
}

.course__offer__label {
  color: var(--ink-subtle);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* The two-line headline. -space-5 cancels the card's flex gap so the headline
   sits under its own eyebrow rather than a paragraph's distance from it — the
   same trick .course__price used before it, for the same reason.

   Two <span>s set to display:block rather than a <br>: the line break is
   typography, and at the narrowest width each line wraps on its own terms
   instead of breaking in a place the markup chose. */
.course__offer__title {
  font-size: var(--text-2xl);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  margin-top: calc(var(--space-5) * -1 + var(--space-2));
}

.course__offer__line {
  display: block;
}

.course__offer__title .accent {
  color: var(--brand);
}

.course__offer__body {
  color: var(--ink-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  /* Absorbs the flex gap: the body belongs to the headline above it, not to
     the feature rows below. */
  margin-top: calc(var(--space-5) * -1 + var(--space-3));
}

/* ---- the three feature rows -------------------------------------------- */

/* Same two-column shape as .benefit in the Everything You Need band — icon
   down the left, heading and blurb to its right — because it is the same
   idea and should not be a second invention. The icon square is smaller
   (36px against 44px) because this one sits inside a card inside a column
   rather than in a card of its own. */
.course__features {
  display: grid;
  gap: var(--space-5);
}

.course__feature {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  align-content: start;
  column-gap: var(--space-4);
  row-gap: var(--space-1);
}

.course__feature__icon {
  grid-row: span 2;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  flex: none;
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-md);
}

.course__feature__icon::before {
  content: "";
  width: 19px;
  height: 19px;
  background: var(--brand);
  -webkit-mask: var(--feature-icon) center / contain no-repeat;
  mask: var(--feature-icon) center / contain no-repeat;
}

.course__feature__name {
  color: var(--ink);
  font-size: var(--text-base);
  font-weight: 700;
  line-height: var(--leading-snug);
}

.course__feature__blurb {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

.course__feature--watch {
  --feature-icon: var(--icon-watch-read);
}

.course__feature--notes {
  --feature-icon: var(--icon-notes);
}

.course__feature--build {
  --feature-icon: var(--icon-shipped);
}

/* Drawn to the same recipe as the --icon-* set further down this file: a
   24x24 box, no fill, 2px round-capped strokes, so they sit in a row with the
   benefit band's six without anyone noticing they were added later. */
:root {
  /* The Guide's page outline with a play triangle on it, because the lesson
     is a video and a page of reading, not one or the other. */
  --icon-watch-read: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3h7l5 5v13H6z'/%3E%3Cpath d='M13 3v5h5'/%3E%3Cpath d='M10.5 11.5l4.5 2.75-4.5 2.75z'/%3E%3C/svg%3E");
  /* A pencil: the student writes, the platform keeps it. */
  --icon-notes: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 4.5l4.5 4.5L9.5 19H5v-4.5z'/%3E%3Cpath d='M13 6.5l4.5 4.5'/%3E%3C/svg%3E");
  /* A closed box seen corner-on: a thing built and shipped, not a certificate
     for having watched. */
  --icon-shipped: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3l8 4.5v9L12 21l-8-4.5v-9z'/%3E%3Cpath d='M4 7.5l8 4.5 8-4.5'/%3E%3Cpath d='M12 12v9'/%3E%3C/svg%3E");
}

/* THE ANTHROPIC PLAN BLOCK USED TO SIT HERE — a tinted, bordered block between
   the feature rows and the button. The owner asked for it gone (issue #13
   follow-up), so its three rules went with it rather than being left behind as
   dead selectors nothing matches. Nothing else on the site used them.

   The card is a flex column with a single `gap`, so removing the block closed
   its own space: the feature list now meets the button on that same gap and
   there is no stranded margin, divider or empty cell where the box was. */

/* The same purchase pill as the header and footer carry, at the size a
   section-level action wants. Still .course-link--buy — one purchase, one
   treatment, everywhere on the site. */
.course__cta {
  padding-block: var(--space-4);
  font-size: var(--text-md);
}

.course__offer__foot {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  text-align: center;
  /* Absorbs the flex gap so the note sits under the button, not adrift. */
  margin-top: calc(var(--space-5) * -1 + var(--space-3));
}

/* ---- narrow screens ---------------------------------------------------- */

/* THE CARD WAS STICKY AND IS NOT ANY MORE. While it held a price, a label and
   a button, pinning it kept the offer in view down the length of the "what
   you'll learn" column beside it. Carrying the Course, it is too tall to pin
   safely — a sticky element taller than its slot pins at the top and leaves
   its own bottom permanently below the fold, and the bottom of this card is
   "Get the Course". The purchase link would be unreachable for the whole time
   the card was pinned.

   Measured in a browser rather than reasoned about, against the 728px a 72px
   header leaves in an 800px-tall viewport:

     960 wide   790px   overflows
     1024 wide  745px   overflows
     1280 wide  678px   fits, with 50px to spare
     1440 wide  678px   fits, with 50px to spare

   So it clears the slot only on a wide viewport that is also tall, and the
   50px of headroom at 1280 is gone on any window shorter than 750px — a
   1366x768 laptop with browser chrome is already under it. Restoring `position:
   sticky` because it happens to fit on the machine you are testing on puts the
   purchase link back under the fold for everyone else. No gate here catches
   that; the only way to know is to open the page and measure. */

@media (max-width: 900px) {
  .course {
    padding-block: var(--space-9);
  }

  .course__grid {
    grid-template-columns: 1fr;
    grid-template-areas:
      "intro"
      "offer";
    gap: var(--space-7);
  }
}

/* =========================================================================
   The Guide — the one block on Home written in the first person

   .jer, NOT .guide-*. CONTEXT.md gives "Guide" exactly one meaning on this
   site (the page that presents all six Phases in depth) and guide.html already
   owns the .guide-* namespace with two dozen selectors. This is the StoryBrand
   guide — the person, not the page — and it is named so the two can never be
   confused in a stylesheet both pages load.

   PROSE, WITH ONE EXHIBIT BESIDE IT. Four paragraphs at a real measure and a
   small card holding the "Built with Claude Code" credit, which moved up here
   from the footer bar (RETOOL_COPY §A4). A credit in a footer is boilerplate;
   a credit next to the paragraph claiming the method works is evidence, and
   that is the only reason it moved.

   ON --surface, between .course's tint and .plan's. Nothing else separates it,
   and nothing else needs to.

   Contrast on --surface: --ink 18.9:1, --ink-body 13.6:1. On the exhibit's
   --brand-tint: --ink 17.1:1, --brand-strong 7.4:1.
   ========================================================================= */

.jer {
  padding-block: var(--space-10) var(--space-9);
}

.jer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 17rem);
  align-items: start;
  gap: var(--space-8) var(--space-9);
}

.jer__title {
  font-size: clamp(1.625rem, 3.6vw, var(--text-2xl));
  margin-bottom: var(--space-6);
}

/* 68ch, not the container. Four paragraphs of continuous prose is the longest
   uninterrupted read on Home, and 1180px of it is unreadable — the same
   measure rule the Guide page states for itself. */
.jer__copy p {
  color: var(--ink-body);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  max-width: 68ch;
}

.jer__copy p + p {
  margin-top: var(--space-5);
}

/* Two phrases the owner asked to see bold (2026-09-10). Ink rather than the
   body grey, the way .success__notes strong reads, so the weight lands. */
.jer__copy strong {
  color: var(--ink);
  font-weight: 700;
}

.exhibit {
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}

.exhibit__label {
  color: var(--brand-strong);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  margin-bottom: var(--space-2);
}

.exhibit__credit {
  color: var(--ink);
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: var(--leading-snug);
}

/* =========================================================================
   The plan — three moves, from paying to having shipped

   THE NUMERALS ARE A COUNTER, NOT CONTENT. A typed "1" is a number a visitor
   reads, and scripts/check-claims.mjs asks every number on this site what
   claim it makes. These make none — they are the shape of an ordered list —
   so they are drawn by CSS and the <ol> carries the order to a screen reader.
   The rail's "01"…"06" are typed because those ordinals ARE the claim (they
   are the Phase numbers) and they are licensed as such.

   ON --surface-sunk, between the white Guide above and the white rail below:
   the last tinted band before the free material starts.

   Contrast on --surface (the cards): --ink-body 13.6:1. On --brand-tint (the
   numeral): --brand-strong 7.4:1.
   ========================================================================= */

.plan {
  background: var(--surface-sunk);
  border-block: 1px solid var(--line);
  padding-block: var(--space-10);
}

.plan__list {
  counter-reset: plan;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
}

.plan__step {
  counter-increment: plan;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  align-content: start;
  column-gap: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-5);
  color: var(--ink-body);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
}

.plan__step::before {
  content: counter(plan);
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: 50%;
  color: var(--brand-strong);
  font-size: var(--text-sm);
  font-weight: 800;
  line-height: 1;
}

@media (max-width: 900px) {
  .jer {
    padding-block: var(--space-9);
  }

  .jer__grid {
    grid-template-columns: 1fr;
    gap: var(--space-7);
  }

  .plan {
    padding-block: var(--space-9);
  }

  .plan__list {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
}

/* =========================================================================
   What you'll learn to do — the success band

   THESE RULES ARE THE COURSE CARD'S OLD `learn` COLUMN, MOVED AND RENAMED.
   RETOOL_COPY Part A puts "What you'll learn to do" at position 6 rather than
   inside the value proposition at 3, so Retool 8 (#16) lifted the block out of
   .course and it brought its styling with it. The declarations are unchanged;
   only the selector prefix is, because a rule named .course__* doing the work
   of a band that is not the Course is the kind of drift CONTEXT.md exists to
   stop.

   TWO COLUMNS AT FULL WIDTH INSTEAD OF ONE AT TWO-THIRDS. In the card the list
   sat in a 1.35fr column beside the offer; on its own it has the container, and
   eleven items in one column would be a very tall band for a page that still
   has nine sections to go.

   ON --surface, BETWEEN THE TINTED PLAN AND THE WHITE RAIL, with its section
   head doing the separating below — the arrangement .rail and .benefits already
   use.

   Contrast on --surface: --ink 18.9:1, --ink-body 13.6:1, --ink-muted 6.7:1.
   ========================================================================= */

.success {
  padding-block: var(--space-10) var(--space-9);
}

.success__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4) var(--space-6);
}

.success__list li {
  position: relative;
  color: var(--ink-body);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  padding-left: calc(var(--space-5) + var(--space-2));
}

.success__list li::before {
  content: "";
  position: absolute;
  left: 0;
  /* Optical, not mechanical: aligns the tick with the first line's x-height. */
  top: 0.22em;
  width: 17px;
  height: 17px;
  background: var(--brand);
  -webkit-mask: var(--icon-check) center / contain no-repeat;
  mask: var(--icon-check) center / contain no-repeat;
}

/* Declared here because the tick is declared here. .prompt__copy's "copied"
   state uses it too, and a custom property resolves wherever it is defined on
   :root — file order does not matter to it. */
:root {
  --icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5l5.5 5.5L20 6'/%3E%3C/svg%3E");
}

.success__notes {
  display: grid;
  gap: var(--space-4);
  margin-top: var(--space-7);
  padding-top: var(--space-6);
  border-top: 1px solid var(--line-strong);
}

.success__notes p {
  color: var(--ink-muted);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  max-width: 62ch;
}

.success__notes strong {
  color: var(--ink);
  font-weight: 700;
}

/* §A6's closing line, and the one thing the band gained. It is the outcome the
   list adds up to, so it is set at the weight of a statement rather than of a
   note, centred under the two columns it closes. */
.success__close {
  max-width: 46rem;
  margin: var(--space-7) auto 0;
  color: var(--ink);
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: var(--leading-snug);
  text-align: center;
}

@media (max-width: 900px) {
  .success {
    padding-block: var(--space-9) var(--space-8);
  }

  .success__list {
    grid-template-columns: 1fr;
    gap: var(--space-3);
  }
}

/* =========================================================================
   The Six-Phase Framework rail

   Six cards in order, on one row where there is room for one, wrapping to
   three, two and one as there stops being. The arrows between them are the
   point of the row and only appear while the row is a row; a column of
   arrows pointing sideways would be decoration pretending to be meaning.

   Contrast on --surface: --ink 18.9:1, --ink-body 13.6:1, --ink-muted 6.7:1,
   --ink-subtle 5.2:1, --brand-strong on --brand-tint 7.4:1.
   ========================================================================= */

.section-head {
  max-width: 46rem;
  margin-inline: auto;
  margin-bottom: var(--space-8);
  text-align: center;
}

.section-head__title {
  font-size: clamp(1.875rem, 4.2vw, var(--text-3xl));
  margin-bottom: var(--space-3);
}

.section-head__lede {
  color: var(--ink-muted);
  font-size: var(--text-lg);
  line-height: var(--leading-snug);
}

.rail {
  padding-block: var(--space-10);
}

/* The acronym glosses (§A7), above the cards that use the acronyms.

   NOT CARDS, AND NOT TINTED. Three definitions are reference material for the
   six cards below them, not a seventh, eighth and ninth thing on offer; they
   get a hairline apiece and the muted ink the site uses for meta. If this strip
   ever competes with the rail for attention it is wrong.

   Contrast on --surface: --ink 18.9:1 (the term), --ink-muted 6.7:1 (the
   gloss). */
.glossary {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
  max-width: 62rem;
  margin: 0 auto var(--space-8);
}

.glossary__item {
  color: var(--ink-muted);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line-strong);
}

.glossary__term {
  color: var(--ink);
  font-weight: 800;
}

@media (max-width: 760px) {
  .glossary {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
}

.rail__list {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-5);
}

.rail__item {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-5) var(--space-4) var(--space-4);
}

.rail__name {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--text-lg);
}

/* The ordinal is inside the heading, not floated beside it: "01 Charter" is
   what a screen reader should read, and it is what the tab below says too. */
.rail__num {
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-sm);
  color: var(--brand-strong);
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: var(--tracking-wide);
  line-height: 1.6;
  padding-inline: var(--space-2);
}

.rail__label {
  line-height: var(--leading-snug);
}

.rail__descriptor {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
}

/* Pushed to the bottom so the six sit on one line however tall the cards get. */
.rail__produces {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.rail__produces__label {
  color: var(--ink-subtle);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* --text-xs, not --text-sm: PROJECT_CHARTER.md is the longest thing on this
   row and at 14px it does not fit a sixth of the container. A filename broken
   across two lines reads as a mistake. */
.rail__produces__doc {
  color: var(--ink-body);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
  overflow-wrap: anywhere;
}

@media (min-width: 1180px) {
  .rail__item + .rail__item::before {
    content: "";
    position: absolute;
    top: 50%;
    left: calc(var(--space-5) * -1);
    width: var(--space-5);
    height: 10px;
    transform: translateY(-50%);
    background: var(--line-strong);
    -webkit-mask: var(--icon-arrow-right) center / 18px 10px no-repeat;
    mask: var(--icon-arrow-right) center / 18px 10px no-repeat;
  }
}

@media (max-width: 1179px) {
  .rail__list {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 700px) {
  .rail {
    padding-block: var(--space-9);
  }

  .rail__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
  }
}

@media (max-width: 460px) {
  .rail__list {
    grid-template-columns: 1fr;
  }
}

/* =========================================================================
   Everything You Need to Succeed — the benefit cards

   Six cards, three columns, wrapping to two and then one. The copy is
   written in home.html and every claim in it was checked against the repo
   before it was written; two of the mockup's six cards did not survive that
   check and are not here. See the comment above the section.

   ON --surface, LIKE THE RAIL ABOVE IT. The page alternates a white band and
   a --surface-sunk one, and the sections either side of this pair are tinted:
   .course above the rail, .preview below this. A tint here would meet
   .preview's tint with a single hairline between them, which reads as a rule
   somebody left behind rather than as two sections. The section head does the
   separating instead, exactly as the rail's does.

   THE ICON IS A MASK OVER A DATA URI, the same mechanism as
   .course-link--return and the rail's arrows: no image request, and the glyph
   takes a colour from the stylesheet rather than from a file. Each card names
   its own glyph in --benefit-icon; .benefit__icon draws the tinted square and
   its ::before draws the glyph inside it.

   Contrast on --surface: --ink 18.9:1, --ink-body 13.6:1, --ink-muted 6.7:1,
   --brand on --brand-tint 5.7:1 (the glyph, which is decorative anyway —
   every card repeats its meaning in the heading beside it).
   ========================================================================= */

.benefits {
  padding-block: var(--space-10) var(--space-9);
}

.benefits__list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
}

/* Two columns: the icon down the left of both rows, the text to its right.
   Not a flex row with a nested column, because the blurb has to wrap under
   the heading and not under the icon. */
.benefit {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  /* The cards in a row are stretched to the tallest of them. Without this,
     the two rows inside a short card stretch with it and the space between
     its heading and its blurb grows — so a three-line card and a four-line
     card beside it end up with visibly different typography. The slack
     collects at the bottom of the card instead. */
  align-content: start;
  column-gap: var(--space-4);
  row-gap: var(--space-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-5);
}

.benefit__icon {
  grid-row: span 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: none;
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-md);
}

.benefit__icon::before {
  content: "";
  width: 22px;
  height: 22px;
  background: var(--brand);
  -webkit-mask: var(--benefit-icon) center / contain no-repeat;
  mask: var(--benefit-icon) center / contain no-repeat;
}

.benefit__name {
  font-size: var(--text-lg);
  line-height: var(--leading-snug);
}

.benefit__blurb {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

/* One glyph per card. --icon-copy is the copy control's own icon, reused
   deliberately: the card describes that control, so it should wear its mark. */
.benefit--guide {
  --benefit-icon: var(--icon-guide);
}

.benefit--prompts {
  --benefit-icon: var(--icon-copy);
}

.benefit--downloads {
  --benefit-icon: var(--icon-download);
}

.benefit--tutorials {
  --benefit-icon: var(--icon-play-frame);
}

.benefit--overview {
  --benefit-icon: var(--icon-path);
}

.benefit--free {
  --benefit-icon: var(--icon-unlocked);
}

:root {
  /* A page with lines on it: the Guide. */
  --icon-guide: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 3h7l5 5v13H6z'/%3E%3Cpath d='M13 3v5h5'/%3E%3Cpath d='M9 13h6M9 17h4'/%3E%3C/svg%3E");
  /* An arrow into a tray: a real file arriving on a real disk. */
  --icon-download: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3v11M7.5 9.5L12 14l4.5-4.5'/%3E%3Cpath d='M4 18h16'/%3E%3C/svg%3E");
  /* A play triangle inside a frame: a filmed Tutorial. */
  --icon-play-frame: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='14' rx='3'/%3E%3Cpath d='M10.5 9.2l4.8 2.8-4.8 2.8z'/%3E%3C/svg%3E");
  /* A start bar, a run, an arrowhead: the Overview is the whole path, end to
     end. Circles were tried first and filled in solid at 22px — three dots
     that read as an ellipsis. */
  --icon-path: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5v11M4 12h13M14 9l3 3-3 3'/%3E%3C/svg%3E");
  /* A padlock standing open: there is nothing here to unlock. */
  --icon-unlocked: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='11' width='16' height='9' rx='2'/%3E%3Cpath d='M8 11V7a4 4 0 0 1 7.7-1.5'/%3E%3C/svg%3E");
}

@media (max-width: 1000px) {
  .benefits__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .benefits {
    padding-block: var(--space-9);
  }

  .benefits__list {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }

  .benefit {
    padding: var(--space-4);
  }
}

/* ---- the agreement strip (§A9) ---------------------------------------- */

/* Four lines of terms under six cards of offer. Set as a row of plain
   statements, not as a seventh card and not as a tinted note: three of them are
   promises the site is making and the fourth is the one thing it asks for, and
   a reader should be able to take all four in without deciding to read them.

   THE FOURTH IS THE ONE THAT COSTS SOMETHING, so it is the one that gets the
   ink. --ink on the required line, --ink-muted on the three that are free.

   Contrast on --surface: --ink 18.9:1, --ink-muted 6.7:1. */
.agreement {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-5);
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: 1px solid var(--line-strong);
}

.agreement__item {
  color: var(--ink-muted);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
}

.agreement__item:last-child {
  color: var(--ink);
  font-weight: 700;
}

@media (max-width: 1000px) {
  .agreement {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .agreement {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
}

/* =========================================================================
   The Phase preview — tabs, panels, and the copy-prompt control

   The mechanism the Guide (#6) reuses. Nothing below is sized to six Phases
   or to a preview-length panel: the tablist wraps, the panel takes whatever
   height its content needs, and the prompt box scrolls past a bound instead
   of pushing the page.

   TWO RULES ARE LOAD-BEARING, not cosmetic:

     `html:not(.js) [data-phase-tablist]` and `[data-copy-prompt]` are hidden
     while there is no JavaScript to make them work, which is what lets the
     served page carry all six Phases open and readable with no dead controls.

     `.prompt__text` keeps `white-space: pre-wrap`. Set it to `normal` and the
     visitor reads a re-flowed prompt while the clipboard still receives the
     author's line breaks — the displayed and the copied prompt stop being the
     same thing, silently. scripts/check-prompts.mjs fails on both.

   Contrast on --surface / --surface-sunk: --ink 18.9 / 17.7, --ink-body
   13.6 / 12.8, --ink-muted 6.7 / 6.3, --ink-subtle 5.2 / 4.9,
   --brand-strong 8.7 / 8.1, --brand 6.7 / 6.3. --brand-strong on the
   selected tab's --brand-tint is 7.4:1 and --brand on it is 5.7:1.
   ========================================================================= */

.preview {
  background: var(--surface-sunk);
  border-block: 1px solid var(--line);
  padding-block: var(--space-10);
}

.preview__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  align-items: start;
  gap: var(--space-9);
}

.preview__title {
  font-size: clamp(1.875rem, 4.2vw, var(--text-3xl));
  margin-bottom: var(--space-5);
}

.preview__lede {
  color: var(--ink-muted);
  font-size: var(--text-md);
  max-width: 44ch;
  margin-bottom: var(--space-5);
}

.preview__points {
  display: grid;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}

.preview__points li {
  position: relative;
  color: var(--ink-body);
  font-size: var(--text-base);
  font-weight: 600;
  line-height: var(--leading-snug);
  padding-left: calc(var(--space-5) + var(--space-2));
}

.preview__points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.22em;
  width: 17px;
  height: 17px;
  background: var(--brand);
  -webkit-mask: var(--icon-check) center / contain no-repeat;
  mask: var(--icon-check) center / contain no-repeat;
}

/* THE WAY OUT OF THE PREVIEW AND INTO THE GUIDE, AND IT IS NOT A PILL ANY
   MORE. It was an outlined pill reading "Explore the Framework"; Retool 8 (#16)
   made it the transitional call to action in its one approved wording, and
   RETOOL_COPY's copy rules forbid that being a button — "Transitional CTA is a
   text link, never a second button". So it takes .hero__alt's treatment
   exactly: underlined in the hairline, brand on hover, and NO drawn arrow,
   because the arrow is a character in the copy. Two arrows a few characters
   apart is what scripts/verify-copy.mjs reads as somebody stating the Phase
   chain. */
.preview__action {
  display: inline-flex;
  align-items: center;
  color: var(--ink-body);
  font-size: var(--text-base);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.32em;
  transition: color var(--transition), text-decoration-color var(--transition);
}

.preview__action:hover {
  color: var(--brand-strong);
  text-decoration-color: currentColor;
}

/* ---- the tabs --------------------------------------------------------- */

.phase-tabs {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

/* Wraps rather than scrolls. A horizontal scroller on a phone hides the
   Phases at its right edge, and hidden tabs are tabs nobody presses. */
.phase-tabs__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-1);
  padding: var(--space-3);
  border-bottom: 1px solid var(--line);
}

.phase-tab {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  color: var(--ink-muted);
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  padding: var(--space-2) var(--space-3);
  transition: color var(--transition), background-color var(--transition),
    border-color var(--transition);
}

.phase-tab:hover {
  background: var(--surface-sunk);
  color: var(--ink);
}

.phase-tab__num {
  color: var(--ink-subtle);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
}

/* Selection is carried by aria-selected, so the thing a screen reader
   announces and the thing an eye sees are driven by one attribute and cannot
   drift apart. Colour is not doing it alone: weight and the filled pill
   change too. */
.phase-tab[aria-selected="true"] {
  background: var(--brand-tint);
  border-color: var(--brand-edge);
  color: var(--brand-strong);
  font-weight: 800;
}

.phase-tab[aria-selected="true"] .phase-tab__num {
  color: var(--brand);
}

/* ---- a Phase --------------------------------------------------------- */

.phase-panel {
  padding: var(--space-6);
}

.phase-panel:focus-visible {
  outline-offset: -3px;
}

/* Only ever seen with JavaScript blocked, where all six are stacked. */
.phase-panel:not([hidden]) + .phase-panel:not([hidden]) {
  border-top: 1px solid var(--line);
}

.phase-panel__title {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: var(--text-xl);
}

.phase-panel__num {
  color: var(--brand);
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: var(--tracking-wide);
}

.phase-panel__tagline {
  color: var(--ink-muted);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  margin-top: var(--space-3);
}

.phase-panel__produces {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.phase-panel__produces__label {
  color: var(--ink-subtle);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.phase-panel__produces__doc {
  color: var(--ink-body);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

/* ---- the prompt, and copying it --------------------------------------- */

.prompt {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}

.prompt__label {
  color: var(--ink-subtle);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  margin-bottom: var(--space-3);
}

.prompt__text {
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--ink-body);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.55;
  padding: var(--space-4);
  /* Bounded, and focusable in the markup so the bound is scrollable by
     keyboard as well as by pointer. */
  max-height: 22rem;
  overflow: auto;
  overscroll-behavior: contain;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  tab-size: 2;
}

.prompt__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.prompt__copy {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--surface);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-pill);
  color: var(--brand-strong);
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  padding: var(--space-2) var(--space-5);
  transition: background-color var(--transition), border-color var(--transition);
}

.prompt__copy::before {
  content: "";
  width: 15px;
  height: 15px;
  flex: none;
  background: currentColor;
  -webkit-mask: var(--icon-copy) center / contain no-repeat;
  mask: var(--icon-copy) center / contain no-repeat;
}

.prompt__copy:hover {
  background: var(--brand-tint);
  border-color: var(--brand);
}

/* Confirmation the eye catches even if the live region is missed. */
.prompt__copy[data-copied="true"] {
  background: var(--brand-tint);
  border-color: var(--brand);
}

.prompt__copy[data-copied="true"]::before {
  -webkit-mask: var(--icon-check) center / contain no-repeat;
  mask: var(--icon-check) center / contain no-repeat;
}

.prompt__status {
  color: var(--brand-strong);
  font-size: var(--text-sm);
  font-weight: 600;
}

/* Empty between copies, and an empty live region should take no room. */
.prompt__status:empty {
  display: none;
}

:root {
  --icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='9' y='9' width='12' height='12' rx='2'/%3E%3Cpath d='M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1'/%3E%3C/svg%3E");
}

/* ---- with JavaScript blocked ------------------------------------------
   The two controls here that cannot work without it. Hidden, not disabled and
   not left dead: every Phase below is open and readable, and the prompts are
   on the page to be selected by hand. Same bargain the phone navigation makes
   further up this file — the markup is the working state and the `js` class
   is what buys the enhanced one.
   ---------------------------------------------------------------------- */

html:not(.js) [data-phase-tablist] {
  display: none;
}

html:not(.js) [data-copy-prompt] {
  display: none;
}

/* ---- narrow screens ---------------------------------------------------- */

@media (max-width: 900px) {
  .preview {
    padding-block: var(--space-9);
  }

  .preview__grid {
    grid-template-columns: 1fr;
    gap: var(--space-7);
  }

  .phase-panel {
    padding: var(--space-5) var(--space-4);
  }

  .phase-tabs__list {
    padding: var(--space-2);
  }

  .phase-tab {
    padding: var(--space-2);
  }
}

@media (max-width: 560px) {
  .prompt__text {
    font-size: var(--text-xs);
    padding: var(--space-3);
  }

  .prompt__actions {
    gap: var(--space-3);
  }
}

/* =========================================================================
   What it costs — the price band (§A8)

   THE SAME BORDERED BAND THE CLOSING CALL TO ACTION USES, and deliberately so:
   copy on the left, the one pink control on the right, a hairline around the
   whole thing. This band and that one are the two places on Home where the
   page stops explaining and asks, and a reader who recognises the shape of the
   second from the first is a reader who knows what is being asked.

   NO PRICE TYPE SCALE. The Course card used to set a figure at --text-4xl and
   the temptation is to do it again here. There is no figure: "$XXX" is three
   characters standing in for a number the visitor chooses, and setting three
   Xs at 50px would make the page look broken rather than generous. The price
   is emphasised inside the sentence instead, at the weight of a name.

   ON --surface, under the tinted Phase preview.

   Contrast on --surface: --ink 18.9:1, --ink-body 13.6:1.
   ========================================================================= */

.price {
  padding-block: var(--space-10);
}

.price__band {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-6) var(--space-8);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-8);
}

.price__title {
  font-size: clamp(1.875rem, 4.2vw, var(--text-3xl));
  margin-bottom: var(--space-4);
}

.price__body {
  color: var(--ink-body);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  max-width: 62ch;
}

.price__body + .price__body {
  margin-top: var(--space-4);
}

.price__figure {
  color: var(--ink);
  font-weight: 800;
  white-space: nowrap;
}

.price__cta {
  font-size: var(--text-base);
  padding: var(--space-4) var(--space-6);
}

@media (max-width: 880px) {
  .price__band {
    grid-template-columns: 1fr;
    padding: var(--space-6);
  }
}

@media (max-width: 560px) {
  .price {
    padding-block: var(--space-9);
  }

  .price__cta {
    width: 100%;
  }
}

/* =========================================================================
   The qualifier — who this is for, and who it is not (§A10)

   TWO PARAGRAPHS AND NO HEADING. §A10 supplies neither a title nor a list, and
   the band is better without one: "This is for you if…" is a heading already.
   So it is set as prose, one measure, centred — the only band on Home that is
   nothing but sentences.

   THE FIRST PARAGRAPH IS SET LARGER THAN THE SECOND. The reader who belongs
   here should meet themselves in the first two lines; the reader who does not
   should reach the second paragraph and stop. Size is how that order is made
   without a heading to make it.

   ON --surface-sunk, between the white free band and the white FAQ.

   Contrast on --surface-sunk: --ink 17.7:1, --ink-body 12.8:1.
   ========================================================================= */

.qualifier {
  background: var(--surface-sunk);
  border-block: 1px solid var(--line);
  padding-block: var(--space-10);
}

.qualifier__copy {
  max-width: 52rem;
  margin-inline: auto;
  text-align: center;
}

.qualifier__lede {
  color: var(--ink);
  font-size: var(--text-xl);
  font-weight: 700;
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
}

.qualifier__body {
  color: var(--ink-body);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  margin-top: var(--space-5);
}

@media (max-width: 760px) {
  .qualifier {
    padding-block: var(--space-9);
  }

  .qualifier__lede {
    font-size: var(--text-lg);
  }
}

/* =========================================================================
   The disclosure: a native <details> and <summary>, dressed once

   ONE PRIMITIVE, USED IN TWO PLACES. The FAQ's questions on Home (§A11) and
   the Tech specs on every Showcase card are the same control: a native
   <details> whose <summary> a click, a keyboard or a screen reader opens, and
   which reads as plain content with JavaScript blocked, because no JavaScript
   is involved at all. What CSS has to do about that is small and specific, so
   it is done here once and each place adds only its own type size and
   spacing:

     · remove the default marker, in both spellings: `list-style: none` on the
       summary for the standard one and ::-webkit-details-marker for Safari's,
       because Safari draws it from a pseudo-element the list-style rule does
       not reach;
     · draw a chevron of our own on the right, rotated when the entry is open,
       so the control still says which way it goes;
     · give the summary a cursor and a hover colour. The focus ring is the
       global one and is not overridden here: a disclosure a keyboard cannot
       see is a disclosure a keyboard user cannot use.

   The markup is `<details class="disclosure">` with
   `<summary class="disclosure__summary">` as its first child; the rotation
   rule uses the child combinator so a disclosure nested inside another only
   turns its own chevron.

   The explainer strip (§A13) still carries its own copy of these rules,
   centred and one size down. Folding it in is a tidy-up for another ticket.
   ========================================================================= */

.disclosure__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  cursor: pointer;
  color: var(--ink);
  font-weight: 700;
  line-height: var(--leading-snug);
  list-style: none;
}

.disclosure__summary::-webkit-details-marker {
  display: none;
}

.disclosure__summary::after {
  content: "";
  width: 14px;
  height: 9px;
  flex: none;
  background: var(--brand-strong);
  -webkit-mask: var(--icon-chevron-down) center / contain no-repeat;
  mask: var(--icon-chevron-down) center / contain no-repeat;
  transition: transform var(--transition);
}

.disclosure[open] > .disclosure__summary::after {
  transform: rotate(180deg);
}

.disclosure__summary:hover {
  color: var(--brand-strong);
}

:root {
  /* A chevron pointing down: closed. Rotated 180° when the entry is open. */
  --icon-chevron-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 9' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5L7 7.5l6-6'/%3E%3C/svg%3E");
}

/* =========================================================================
   Questions, answered — the FAQ (§A11)

   NATIVE <details> AND <summary>, AND NO JAVASCRIPT AT ALL. See the note above
   the band in home.html for why. Each entry is a .disclosure (above); what is
   left here is the FAQ's own size and rhythm.

   ROWS, NOT CARDS. Five bordered cards would be five things; five rows sharing
   a hairline are one list a reader can run down. The whole list is capped at a
   reading measure and centred, because an answer set to 1180px is a paragraph
   nobody finishes.

   Contrast on --surface: --ink 18.9:1 (the question), --ink-body 13.6:1 (the
   answer).
   ========================================================================= */

.faq {
  padding-block: var(--space-10);
}

.faq__list {
  max-width: 52rem;
  margin-inline: auto;
  border-top: 1px solid var(--line);
}

.faq__item {
  border-bottom: 1px solid var(--line);
}

.faq__question {
  font-size: var(--text-lg);
  letter-spacing: var(--tracking-tight);
  padding-block: var(--space-5);
}

.faq__answer {
  padding-bottom: var(--space-5);
}

.faq__answer p {
  color: var(--ink-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  max-width: 68ch;
}

@media (max-width: 560px) {
  .faq {
    padding-block: var(--space-9);
  }

  .faq__question {
    font-size: var(--text-md);
  }
}

/* =========================================================================
   The Build on Home: the featured strip (#24)

   JER'S PARAGRAPHS, A ROW OF CARDS, ONE LINK OUT. This band used to hold a
   dashed coming-soon panel (.soon, #8 and #16), and the Showcase page was
   built out of the same panel before it had cards (#19). Both surfaces are
   published now, the panel and its clock glyph are gone from the stylesheet,
   and scripts/check-links.mjs check 8 holds both surfaces to that. What
   stands here instead is a strip of the builds Jer chose to feature, each a
   link at its own card on the Showcase, showing the screenshot that card
   shows.

   ONE ROW, HOWEVER MANY ARE FEATURED. On a desktop the strip is a single row
   of equal columns, as many as there are cards in it: the count is the
   owner's list, it grows by one when Chroma's card lands (#26), and this
   stylesheet does not change for it. Below 1000px the strip becomes rows of
   three, and on a phone rows of two, which is narrower than the Tutorials go
   (one to a row) because a featured card is a picture and a name rather than
   a card with a blurb, and two across keeps the band short on a phone.

   THE FRAME IS THE SHOWCASE'S, SMALLER: 16:10 with the capture covering it
   from the top edge, exactly as .project__frame does, so a visitor who
   presses through finds the same picture larger. A phone-first build
   (--phone) stands its shot in the frame at its own proportions on the
   tinted backdrop, the way the Showcase's phone frame does, because a phone
   screen cropped to a landscape frame is a picture of nothing. Flex rather
   than grid on that frame for the reason .project--phone gives: a flex
   container resolves the image's percentage height against its own, which
   the aspect ratio makes definite.

   THE WHOLE CARD IS ONE LINK, image and name, the way a Tutorial card's
   frame and name are one link: a large target and one thing to press. It is
   an ordinary href to a fragment on the Showcase, and js/site.js knows
   nothing about it.

   THE LINK OUT IS THE PILL THE OLD PANEL USED, renamed for the band it sits
   in rather than the panel that is gone. It reads "See the Showcase" and is
   not a call to action: the page has one direct CTA label and it is on the
   pink pill, so this one stays on --surface with a brand edge.

   ON --surface, BETWEEN TWO BANDS. .faq above this is white and .explainer
   below it is tinted, so this band's section head does the separating upward
   and the tint does it downward, the same arrangement .rail and .benefits
   use further up this file.

   Contrast on --surface: --ink 18.9:1 (the names), --ink-body 13.6:1 (the
   paragraphs), --brand-strong on --surface 8.9:1 (the link out).
   ========================================================================= */

.build {
  padding-block: var(--space-10) var(--space-9);
}

/* Centred, and as wide as the strip of cards beneath it: the owner's
   instruction of 2026-09-10 ("justified center", running from the first card
   to the last). It used to sit left under the centred head at 66ch, the way
   the rail's glossary does; the two paragraphs are now short enough to read
   as a caption for the strip rather than as a manifesto, which is what the
   width and the alignment make them. */
.build__intro {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  text-align: center;
  margin-bottom: var(--space-7);
}

.build__intro p {
  color: var(--ink-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
}

/* Column flow with no template: every item gets its own equal column, so the
   row is as wide as the list is long. The breakpoints below switch to row
   flow over a fixed template. */
.featured {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: var(--space-5);
}

.featured__item {
  min-width: 0;
}

.featured__link {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  height: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  color: var(--ink);
  padding: var(--space-3);
  text-decoration: none;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.featured__link:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow-md);
}

.featured__frame {
  display: grid;
  aspect-ratio: 16 / 10;
  background: linear-gradient(155deg, var(--brand-tint), var(--surface-sunk) 72%);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.featured__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
}

.featured__frame--phone {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2);
}

.featured__frame--phone img {
  width: auto;
  height: 100%;
  object-fit: contain;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
}

.featured__name {
  font-size: var(--text-base);
  font-weight: 800;
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-snug);
  padding: 0 var(--space-1) var(--space-1);
}

.featured__link:hover .featured__name {
  color: var(--brand-strong);
  text-decoration: underline;
}

/* The same pill the Phase preview uses to leave for the Guide. Written out
   rather than borrowing .preview__action, because a rule named for one
   section should not be load-bearing in another. */
.build__action {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-7);
  background: var(--surface);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-pill);
  color: var(--brand-strong);
  font-size: var(--text-base);
  font-weight: 700;
  padding: var(--space-3) var(--space-5);
  text-decoration: none;
  transition: border-color var(--transition), background-color var(--transition);
}

.build__action:hover {
  background: var(--brand-tint);
  border-color: var(--brand);
}

.build__action__arrow {
  width: 18px;
  height: 10px;
  flex: none;
  background: currentColor;
  -webkit-mask: var(--icon-arrow-right) center / contain no-repeat;
  mask: var(--icon-arrow-right) center / contain no-repeat;
  transition: transform var(--transition);
}

.build__action:hover .build__action__arrow {
  transform: translateX(3px);
}

/* ---- narrow screens ---------------------------------------------------- */

@media (max-width: 1000px) {
  .featured {
    grid-auto-flow: row;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .build {
    padding-block: var(--space-9);
  }

  .featured {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
  }
}

/* =========================================================================
   The Showcase: cards for the builds (#19)

   THREE GROUPS, ONE CARD SHAPE. Apps, Games and Sites are each a heading over
   a grid of .project cards: three across where there is room, two where
   there is not, one on a phone, on the same breakpoints the Tutorials use on
   Home, because a visitor who has seen those cards should recognise these.
   Each card is hand-written markup, one <article> and one image, with no
   build step behind it (ADR-0001); the attributes the gates read are
   data-showcase-group on the section and data-showcase-card on the card.

   THE IMAGE IS A REAL SCREENSHOT, AND THE FRAME IS SHAPED FOR IT. A desktop
   build gets a 16:10 frame the screenshot fills edge to edge. A phone-first
   build (.project--phone) gets a taller frame with the phone shot standing in
   it at its own proportions on a tinted backdrop, because a phone screen
   cropped to a landscape frame is a picture of nothing. Every <img> declares
   its width and height so the grid does not jump as the lazy images arrive,
   and scripts/check-showcase.mjs checks those two numbers against the file.

   TECH SPECS ARE THE FAQ'S DISCLOSURE. The <details> at the foot of each card
   is a .disclosure (see the FAQ), so it opens, closes and focuses exactly the
   way a question on Home does, and reads as plain content with JavaScript
   blocked. The stack inside is rows on hairlines rather than bullets: one row
   per line of the stack, so a one-line stack is one row rather than a lonely
   bullet. The disclosure is pushed to the bottom of the card, the way the
   Tutorial cards push their tags, so three cards of unequal summary length
   still line their Tech specs up.

   THE MARKER. A build that went through the six Phases carries a small
   "Planned with the Framework" pill under its name: the same tint and edge
   the eyebrow uses, one size down. It is a fact about the build, not a badge
   of quality, which is why it is quiet.

   Contrast on --surface: --ink 18.9:1, --ink-body 13.6:1, --ink-muted 6.7:1,
   --brand-strong on --brand-tint 7.4:1 (the marker), --brand-strong on
   --surface 8.9:1 (the live link).
   ========================================================================= */

.showcase-group {
  padding-block: var(--space-9);
  border-top: 1px solid var(--line);
}

.showcase-group__title {
  font-size: clamp(1.625rem, 3.4vw, var(--text-2xl));
  margin-bottom: var(--space-6);
}

/* The one line a group carries while it has no cards. Muted, because it is a
   note about the page rather than something on it. */
.showcase-group__note {
  color: var(--ink-muted);
  font-size: var(--text-base);
  max-width: 54ch;
}

.project-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
}

.project {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  height: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-4);
  transition: border-color var(--transition), box-shadow var(--transition);
}

.project:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow-md);
}

/* Every card carries id="<slug>", so Home's featured strip (#24) can link at
   it by fragment. The jump lands clear of the sticky header because html
   already scroll-pads for it, which is why there is no scroll-margin here:
   the two would add up. :target marks the card the visitor arrived at, in
   the brand the frames already wear an edge of, and it outranks :hover by
   coming after it. */
.project:target {
  border-color: var(--brand);
}

/* The desktop frame: 16:10, the screenshot covering it from the top edge so
   that what a visitor sees is the top of the page, which is what a visitor
   opening the build would see. The gradient behind it is the Tutorials'. */
.project__frame {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  background: linear-gradient(155deg, var(--brand-tint), var(--surface-sunk) 72%);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.project__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
}

/* Every frame is the same 16:10 box, by the owner's decision: a row of cards
   reads as one row whatever each screenshot's shape. A desktop capture that
   is wider than 16:10 (Call Shotgun, one of Jer's own screenshots) would lose
   the first letter of its headline to the crop, so --whole shows it entire
   inside the same box instead, on the gradient. */
.project__frame--whole img {
  object-fit: contain;
}

/* The phone frame: the same 16:10 box as every other card, and the shot stands
   in it at its own proportions
   with a hairline and a lift so it reads as a device, not as a stretched
   picture. FLEX, NOT GRID, and on purpose: a grid row sizes itself to the
   image's intrinsic height first, so `height: 100%` on the image resolved
   against a row taller than the frame and the frame clipped it to a crop.
   A flex container resolves that percentage against its own height, which
   the aspect ratio makes definite, so the image is pinned to the frame and
   its width follows from the declared ratio. */
.project--phone .project__frame {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 16 / 10;
  padding: var(--space-4);
}

.project--phone .project__frame img {
  width: auto;
  height: 100%;
  object-fit: contain;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}

/* Two images in one phone frame (EV Charger Map): the phone shot stands at
   full height exactly as above, and the build's own link preview graphic sits
   beside it in whatever width is left, at its own proportions, centred on the
   phone's midline. A landscape preview in a portrait frame comes out small,
   and that is the intended reading: the phone view is the picture of the
   build (issue #18) and the preview is the second picture, not an equal.
   min-width: 0 is what lets a flex item shrink below its image's own width. */
.project--phone .project__frame--pair {
  gap: var(--space-3);
}

.project--phone .project__frame--pair .project__preview {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  height: auto;
  align-self: center;
}

/* Two images in one desktop frame (Big Brother Premiere, Plan the Exit): the
   capture fills the frame exactly as any desktop frame does, and the build's
   own link preview graphic sits inset over its lower right corner, whole and
   at its own proportions, on a hairline, a ring of surface and a lift, so it
   reads as a card laid on the page rather than part of it. Inset rather than
   side by side because at card width two images across would each be half a
   frame wide and the preview's text would go unreadable; over the corner it
   keeps more than half the frame's width, and the top of the page, which is
   what the capture is for, stays clear. Scoped to cards that are not phones,
   because the phone pair above is a different arrangement for a different
   shape. */
.project:not(.project--phone) .project__frame--pair {
  position: relative;
}

.project:not(.project--phone) .project__frame--pair .project__preview {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-3);
  width: 58%;
  height: auto;
  object-fit: contain;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: 0 0 0 2px var(--surface), var(--shadow-md);
}

.project__name {
  font-size: var(--text-lg);
  font-weight: 800;
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-snug);
}

.project__marker {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-pill);
  color: var(--brand-strong);
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: var(--tracking-wide);
  line-height: 1.6;
  text-transform: uppercase;
  padding: var(--space-1) var(--space-3);
}

/* An ordinary link to the live build, in a new tab. The arrow is the same
   asset the Build band's action uses, so "go somewhere" looks the same
   everywhere on the site. */
.project__link {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: var(--space-2);
  color: var(--brand-strong);
  font-size: var(--text-sm);
  font-weight: 700;
  text-decoration: none;
}

.project__link::after {
  content: "";
  width: 16px;
  height: 9px;
  flex: none;
  background: currentColor;
  -webkit-mask: var(--icon-arrow-right) center / contain no-repeat;
  mask: var(--icon-arrow-right) center / contain no-repeat;
  transition: transform var(--transition);
}

.project__link:hover {
  text-decoration: underline;
}

.project__link:hover::after {
  transform: translateX(3px);
}

.project__summary {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.project__summary p {
  color: var(--ink-body);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

/* One line under a summary that points somewhere on this site. Three Leaf
   Loans sends the visitor to the Guide, which tells that build's story from
   its Charter to its UAT. Muted like the group note, because it is about the
   build rather than part of Jer's summary; the link keeps the site's ordinary
   link colour and opens in the same tab, because it stays on the site. */
.project__note {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

.project__specs {
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.project__specs .disclosure__summary {
  font-size: var(--text-sm);
  padding-block: var(--space-2);
}

.project__stack {
  margin-top: var(--space-2);
}

.project__stack li {
  color: var(--ink-body);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  padding-block: var(--space-2);
  border-top: 1px solid var(--line);
}

@media (max-width: 1000px) {
  .project-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .showcase-group {
    padding-block: var(--space-8);
  }

  .project-list {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
}

/* =========================================================================
   The explanatory paragraph, folded away (§A13)

   A SLIM TINTED STRIP AND NOTHING ELSE. Closed it is one line; open it is one
   paragraph. It carries no heading, no section head and no card, because it is
   not a band a reader is meant to stop at — it is the whole argument of the
   page kept available for anyone who wants it stated once, whole.

   THE SUMMARY IS THE SAME DISCLOSURE THE FAQ USES, at a smaller size and
   centred. Same marker removal in both spellings, same chevron, same rotation
   when open. It borrows the .disclosure primitive's chevron token rather
   than declaring a second one: two identical arrow assets is how a
   stylesheet starts to drift. (Making it a .disclosure outright is the
   tidy-up noted on that block.)

   ON --surface-sunk, between the white Build band and the white Tutorials.

   Contrast on --surface-sunk: --ink 17.7:1, --ink-body 12.8:1.
   ========================================================================= */

.explainer {
  background: var(--surface-sunk);
  border-block: 1px solid var(--line);
  padding-block: var(--space-7);
}

.explainer__more {
  max-width: 60rem;
  margin-inline: auto;
}

.explainer__summary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  cursor: pointer;
  color: var(--ink);
  font-size: var(--text-base);
  font-weight: 700;
  list-style: none;
}

.explainer__summary::-webkit-details-marker {
  display: none;
}

.explainer__summary::after {
  content: "";
  width: 14px;
  height: 9px;
  flex: none;
  background: var(--brand-strong);
  -webkit-mask: var(--icon-chevron-down) center / contain no-repeat;
  mask: var(--icon-chevron-down) center / contain no-repeat;
  transition: transform var(--transition);
}

.explainer__more[open] .explainer__summary::after {
  transform: rotate(180deg);
}

.explainer__summary:hover {
  color: var(--brand-strong);
}

.explainer__body {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line-strong);
}

.explainer__body p {
  color: var(--ink-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
}

/* =========================================================================
   Tutorials — the four that were filmed

   Four cards on one row where there is room, two where there is not, one on a
   phone. FOUR IS NOT AN ARBITRARY COLUMN COUNT: there are four Tutorials
   because four videos exist, and the row is built to look deliberate at that
   width rather than to look like a six-column grid with two tiles missing.
   Unfilmed walkthroughs are not rendered at all — see home.html and
   scripts/check-tutorials.mjs.

   THE FRAME IS DRAWN, NOT PHOTOGRAPHED. There is no thumbnail set that
   belongs to this design, so the card draws a tinted 16:9 panel with a play
   mark and the runtime on it. It reads as a video because of those two
   things, and it costs no bytes. When four real thumbnails exist, an <img>
   goes inside .tutorial__frame and the gradient becomes its backdrop.

   Contrast on --surface: --ink 18.9:1, --ink-body 13.6:1, --ink-muted 6.7:1,
   --surface on --ink (the runtime badge) 18.9:1, --brand-strong on
   --brand-tint 7.4:1.
   ========================================================================= */

.tutorials {
  padding-block: var(--space-10);
}

.tutorials__list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-5);
}

.tutorial {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-4) var(--space-4) var(--space-4);
  transition: border-color var(--transition), box-shadow var(--transition);
}

.tutorial:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow-md);
}

/* The whole thumbnail and the name are one link, so the target is large and
   there is only one thing to press. An ordinary href to youtu.be: with
   JavaScript blocked it behaves exactly as it does with it on. */
.tutorial__watch {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  color: var(--ink);
  text-decoration: none;
}

.tutorial__frame {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  background: linear-gradient(155deg, var(--brand-tint), var(--surface-sunk) 72%);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.tutorial__play {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  background: var(--brand);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-brand);
  color: var(--surface);
  transition: transform var(--transition);
}

.tutorial__play svg {
  width: 20px;
  height: 20px;
  margin-left: 2px; /* optical centring: a triangle's mass sits left of centre */
}

.tutorial__watch:hover .tutorial__play {
  transform: scale(1.08);
}

/* On the frame rather than beside the title: it is a property of the video,
   and this is where every video player in the world puts it. */
.tutorial__runtime {
  position: absolute;
  left: var(--space-3);
  bottom: var(--space-3);
  background: var(--ink);
  border-radius: var(--radius-sm);
  color: var(--surface);
  font-size: var(--text-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.7;
  padding-inline: var(--space-2);
}

.tutorial__name {
  font-size: var(--text-lg);
  font-weight: 800;
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-snug);
}

.tutorial__watch:hover .tutorial__name {
  color: var(--brand-strong);
  text-decoration: underline;
}

.tutorial__blurb {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
}

/* Pushed to the bottom so four cards of unequal blurb length still line their
   tags up — the same trick .rail__produces plays one section up. */
.tutorial__tag {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
  color: var(--ink-body);
  font-size: var(--text-sm);
  font-weight: 700;
}

/* The same pill as the Framework rail's ordinal, and deliberately so: "02" on
   a Tutorial card and "02" on the rail mean the same Phase. */
.tutorial__tag__num {
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-sm);
  color: var(--brand-strong);
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: var(--tracking-wide);
  line-height: 1.6;
  padding-inline: var(--space-2);
}

/* Setup precedes Phase 01 rather than belonging to it, so its tag carries no
   ordinal and must not look like one that lost its number. */
.tutorial__tag--pre {
  color: var(--ink-subtle);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

@media (max-width: 1000px) {
  .tutorials__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .tutorials {
    padding-block: var(--space-9);
  }

  .tutorials__list {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
}

/* =========================================================================
   Downloads — six real files at six real URLs

   Every card is one <a href download> at a path on this host. Nothing here
   runs, nothing is generated, and the section behaves identically with
   JavaScript blocked — which is the entire reason it exists, since the frozen
   live page built these files in the browser as Blobs.

   The link is stretched over the whole card with a pseudo-element, so the
   pressable area is the card while the accessible name stays the file's name
   rather than "card". The name, the note and the size are all inside that
   target; nothing else in the card is interactive, so nothing is trapped
   under it.

   Contrast on --surface (the cards sit on the tinted band in white cards):
   --ink 18.9:1, --ink-muted 6.7:1, --ink-subtle 5.2:1, --brand-strong on
   --brand-tint 7.4:1.
   ========================================================================= */

.downloads {
  background: var(--surface-sunk);
  border-block: 1px solid var(--line);
  padding-block: var(--space-10);
}

.downloads__list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
}

.download {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-5);
  transition: border-color var(--transition), box-shadow var(--transition);
}

.download:hover {
  border-color: var(--brand-edge);
  box-shadow: var(--shadow-md);
}

.download__name {
  font-size: var(--text-md);
  line-height: var(--leading-snug);
}

.download__link {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--ink);
  text-decoration: none;
}

/* The same downward arrow the Guide puts on .guide-file, so a file link looks
   like a file link on both pages. */
.download__link::before {
  content: "\2193";
  color: var(--brand);
  font-weight: 800;
}

/* Stretched over the card. Inset 0 of the positioned .download. */
.download__link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.download:hover .download__link {
  color: var(--brand-strong);
  text-decoration: underline;
}

.download__note {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
}

.download__meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-4);
  color: var(--ink-subtle);
  font-size: var(--text-sm);
}

.download__format {
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-sm);
  color: var(--brand-strong);
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: var(--tracking-wide);
  line-height: 1.6;
  padding-inline: var(--space-2);
}

/* Measured, not typed: see scripts/sources.mjs. Tabular figures so six sizes
   in a column line up on the decimal point. */
.download__size {
  font-variant-numeric: tabular-nums;
}

@media (max-width: 1000px) {
  .downloads__list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .downloads {
    padding-block: var(--space-9);
  }

  .downloads__list {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }

  .download {
    padding: var(--space-4);
  }
}

/* =========================================================================
   Contact — the form (#12)

   ON --surface, between the tinted Downloads band and the white closing
   section. White is not a default here: .downloads and .site-footer are both
   --surface-sunk, and a third tinted band between them would put two identical
   hairlines against each other and run one unbroken grey from the last
   download to the copyright line. The form is a bordered card on white
   instead — the same treatment as .price__band, for the same reason: a panel
   that has to look like a distinct object without a tint to make it one.

   THE FOCUS RING IS THE GLOBAL ONE AND IS NOT OVERRIDDEN. Three form controls
   and a submit button are the densest run of focusable things on the site, and
   they are the place where "visible focus" stops being a nicety. Nothing below
   sets `outline: none`, and the card leaves room around each control so the
   3px ring is never clipped by its neighbour.

   NOTHING HERE DEPENDS ON JAVASCRIPT, including the validation styling: it is
   :user-invalid, which the browser applies only after a person has actually
   interacted with a control, so a form nobody has touched yet is not red.
   :user-invalid is unsupported in older browsers, where the rule is simply
   ignored and the field keeps its resting border — the browser's own
   validation message still appears either way, so no support is required for
   the form to be usable.

   Contrast on --surface: --ink 18.9:1 (labels), --ink-body 13.6:1 (input
   text), --ink-muted 6.7:1 (the note under the button), --brand-strong 8.7:1.
   White on --brand (the Send button) is 6.7:1. --danger on white is 6.5:1.
   ========================================================================= */

.contact {
  padding-block: var(--space-10);
}

.contact__form {
  max-width: 46rem;
  margin-inline: auto;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-7);
}

/* The spam trap. formsubmit.co: "The input should be hidden with CSS."
   scripts/check-contact.mjs asserts this rule is in the served stylesheet,
   because deleting it turns the honeypot into a visible unlabelled text box
   that silently discards the message of anyone who fills it in. */
.contact__honey {
  display: none;
}

/* Name and email share a row from 620px up. They are the two short fields and
   they belong together; the message below them takes the full width because a
   textarea in a half-column is a box nobody wants to type in. */
.contact__pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
}

/* Vertical rhythm between the form's own top-level blocks.
   SCOPED WITH `>` DELIBERATELY. `.field + .field` is the obvious spelling and
   it is wrong: the two halves of .contact__pair are adjacent .field siblings,
   so it pushed the email field a whole row-gap below the name field sitting
   next to it. Every gate stayed green through that — it is a layout defect,
   and it took reading the page in a browser to see it. Inside the pair the
   grid's own gap does the spacing; this rule is only for what follows it. */
.contact__form > * + .field {
  margin-top: var(--space-5);
}

.field__label {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 700;
  margin-bottom: var(--space-2);
}

/* "Required" and "Optional", set as words rather than as an asterisk. Quiet
   enough not to compete with the label, loud enough to read at 12px. */
.field__flag {
  color: var(--brand-strong);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.field__flag--optional {
  color: var(--ink-subtle);
}

.field__control {
  display: block;
  width: 100%;
  background: var(--surface);
  color: var(--ink-body);
  font-family: inherit;
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  transition: border-color var(--transition), box-shadow var(--transition);
}

/* min-height rather than a fixed height, so the rows attribute stays the
   floor and a browser that sizes textareas differently does not clip. */
.field__control--area {
  min-height: 8.5rem;
  resize: vertical;
}

.field__control::placeholder {
  color: var(--ink-subtle);
}

.field__control:hover {
  border-color: var(--ink-subtle);
}

.field__control:focus {
  border-color: var(--brand);
  outline-color: var(--focus);
}

/* Only after the visitor has actually typed something — see the note above. */
.field__control:user-invalid {
  border-color: var(--danger);
}

.contact__foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-5);
  margin-top: var(--space-6);
}

/* Brand purple, and deliberately NOT the pink pill. Pink belongs to Get the
   Course and to nothing else on this site (see the --pink tokens at the top of
   this file); a pink Send would put the purchase colour on a control that does
   not sell anything. */
.contact__submit {
  background: var(--brand);
  color: #ffffff;
  font-family: inherit;
  font-size: var(--text-base);
  font-weight: 700;
  line-height: 1;
  padding: var(--space-4) var(--space-7);
  border: 1px solid var(--brand);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-brand);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}

.contact__submit:hover {
  background: var(--brand-strong);
  border-color: var(--brand-strong);
}

.contact__note {
  flex: 1 1 22ch;
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
}

@media (max-width: 620px) {
  .contact {
    padding-block: var(--space-9);
  }

  .contact__form {
    padding: var(--space-5);
  }

  .contact__pair {
    grid-template-columns: 1fr;
  }

  /* Full width, matching .price__cta and .closing__cta on a phone: a pill that
     stops halfway across a 375px screen reads as an afterthought next to two
     full-width inputs. The note then wraps underneath it rather than beside. */
  .contact__submit {
    width: 100%;
  }
}

/* =========================================================================
   The closing call to action

   The last thing on Home, and the thing that stops the page from ending in a
   gap. Before it existed the Downloads band handed straight over to
   .site-footer, and the footer's own top margin put 6.5rem of white body
   between two tinted bands: a stripe that read as the page having run out
   rather than having finished. This band lands there, and the margin below
   goes away — see the note on .site-footer.

   THE BAND IS A CARD ON A WHITE SECTION, not another full-width tint. The
   Downloads band above it and the footer below it are both --surface-sunk;
   a third tint between them would be one unbroken grey run from the last
   download to the copyright line. --brand-tint instead, which is the only
   surface on this site that is neither white nor grey, so the last thing a
   visitor scrolls past is the one that looks least like the rest.

   Contrast on --brand-tint: --ink 17.1:1, --ink-body 12.3:1, --brand-strong
   7.4:1. White on --brand (the pill) is 6.7:1.
   ========================================================================= */

.closing {
  padding-block: var(--space-10);
}

.closing__band {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-6) var(--space-8);
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-lg);
  padding: var(--space-8);
}

.closing__title {
  font-size: clamp(1.625rem, 3.6vw, var(--text-2xl));
  margin-bottom: var(--space-3);
}

.closing__lede {
  color: var(--ink-body);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  max-width: 56ch;
}

.closing__actions {
  display: flex;
  align-items: center;
  gap: var(--space-6);
}

/* A size up from the header's pill. It is the same button and the same
   label; it is simply the last one a visitor will be offered. */
.closing__cta {
  font-size: var(--text-base);
  padding: var(--space-4) var(--space-6);
}

/* The way back into the free material, and deliberately not a second pill:
   two filled buttons side by side is the pattern that makes a visitor read
   neither.

   THE ::after ARROW CAME OFF IN RETOOL 8 (#16). §A15 gives this link the same
   string the hero and the Phase preview carry, and that string ends in an arrow
   character. Drawing a second one beside it would put two arrows a few
   characters apart, which scripts/verify-copy.mjs reads as a statement of the
   Phase chain — and it would look like a typo before it ever reached the gate.
   It also no longer wraps: "Read the whole framework free →" is too long to
   pin to one line beside the pill on a narrow window. */
.closing__alt {
  display: inline-flex;
  align-items: center;
  color: var(--ink-body);
  font-size: var(--text-sm);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 0.32em;
  transition: color var(--transition), text-decoration-color var(--transition);
}

.closing__alt:hover {
  color: var(--brand-strong);
  text-decoration-color: currentColor;
}

@media (max-width: 880px) {
  .closing__band {
    grid-template-columns: 1fr;
    padding: var(--space-6);
  }

  .closing__actions {
    flex-wrap: wrap;
    gap: var(--space-5);
  }
}

@media (max-width: 560px) {
  .closing {
    padding-block: var(--space-9);
  }

  .closing__cta {
    width: 100%;
  }
}

/* =========================================================================
   Footer
   ========================================================================= */

/* NO TOP MARGIN, AND THAT IS THE FIX FOR A VISIBLE DEFECT. It used to be
   6.5rem, and on both pages it fell between two tinted bands — the Downloads
   band and this one on Home, the last Phase band and this one on the Guide.
   A stripe of white body between two greys reads as the page having stopped
   rather than having ended. Both pages now finish on a section that carries
   its own bottom padding, so the join needs no margin at all. */
.site-footer {
  background: var(--surface-sunk);
  border-top: 1px solid var(--line);
}

/* One hairline at that join, not two. This footer always draws its own top
   border; a last section that also draws a bottom one (the Guide does) would
   put two identical rules against each other and read as a 2px line nobody
   asked for. Home's closing band is white and draws none, so this changes
   nothing there — it is here so that the next page to end on a tinted band
   does not have to rediscover it. */
main > :last-child {
  border-bottom: 0;
}

.site-footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1.2fr);
  gap: var(--space-8) var(--space-7);
  padding-block: var(--space-9) var(--space-8);
}

.site-footer__blurb {
  color: var(--ink-muted);
  font-size: var(--text-base);
  max-width: 34ch;
  margin-top: var(--space-4);
}

.site-footer__heading {
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 800;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  margin-bottom: var(--space-4);
}

.site-footer__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.site-footer__links a {
  color: var(--ink-muted);
  font-size: var(--text-base);
  text-decoration: none;
}

.site-footer__links a:hover {
  color: var(--brand);
  text-decoration: underline;
}

.site-footer__note {
  color: var(--ink-muted);
  font-size: var(--text-base);
  max-width: 36ch;
  margin-bottom: var(--space-5);
}

.site-footer__course {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
}

.site-footer__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-5);
  border-top: 1px solid var(--line);
  color: var(--ink-subtle);
  font-size: var(--text-sm);
}

@media (max-width: 900px) {
  .site-footer__top {
    grid-template-columns: 1fr;
    gap: var(--space-7);
  }
}

/* =========================================================================
   The Guide — all six Phases in depth

   guide.html reuses the Phase tabs above VERBATIM: same attributes, same
   js/site.js, same rules. Everything here is the content INSIDE a panel,
   plus two additions the depth of this page needs:

     · the tablist sticks under the site header from 701px up, so a reader
       eight screens into Phase 05 can switch Phase without scrolling back;
     · below 701px it does not stick — six wrapped pills would eat a third of
       a phone screen — and each Phase ends with a link back up to the list
       instead. That link is the phone's Phase navigation, so it is not
       decoration and must not be styled away.

   MEASURE. The container is 1180px and prose at 1180px is unreadable, so text
   is capped at 68ch inside a panel while tables and preformatted blocks are
   free to use the width and scroll inside themselves.

   Contrast on --surface / --surface-sunk / --brand-tint:
   --ink 18.9 / 17.7 / 16.2, --ink-body 13.6 / 12.8 / 11.6,
   --ink-muted 6.7 / 6.3 / 5.7, --ink-subtle 5.2 / 4.9 / 4.4,
   --brand-strong 8.7 / 8.1 / 7.4. Every pairing used below clears AA (4.5:1);
   --ink-subtle is the one that would not on --brand-tint, and is never put
   there.
   ========================================================================= */

.guide-hero {
  padding-block: var(--space-10) var(--space-9);
}

/* The measure is set on the text, not on the .container this shares a div
   with: capping the container itself would centre the hero in the viewport
   and leave it out of line with every section below it. */
.guide-hero__title,
.guide-hero__lede,
.guide-hero__note {
  max-width: 46rem;
}

.guide-hero__title {
  font-size: clamp(2.25rem, 6vw, var(--text-4xl));
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
  margin-bottom: var(--space-5);
}

.guide-hero__lede {
  color: var(--ink-body);
  font-size: var(--text-lg);
  line-height: var(--leading-normal);
  margin-bottom: var(--space-4);
}

.guide-hero__note {
  color: var(--ink-muted);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
}

.guide {
  background: var(--surface-sunk);
  border-block: 1px solid var(--line);
  padding-block: var(--space-9) var(--space-10);
}

.guide__heading {
  font-size: var(--text-2xl);
  margin-bottom: var(--space-3);
}

.guide__lede {
  color: var(--ink-muted);
  font-size: var(--text-md);
  max-width: 56ch;
  margin-bottom: var(--space-6);
}

/* The anchor the phone's "Back to the Phase list" link jumps to. Without the
   margin the sticky header lands on top of the first row of tabs. */
.guide-tabs {
  scroll-margin-top: calc(var(--header-height) + var(--space-4));
}

@media (min-width: 701px) {
  .guide-tabs .phase-tabs__list {
    position: sticky;
    top: var(--header-height);
    z-index: 20;
    background: var(--surface);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }
}

/* ---- inside a Phase ---------------------------------------------------- */

.guide-panel {
  /* Set on the panel, not on `p`, so that every labelled paragraph below —
     a bug lesson, a requirement id, a download note — overrides it by
     inheritance rather than by out-specifying a descendant selector. */
  color: var(--ink-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  padding: var(--space-7);
}

.guide-panel p,
.guide-panel li,
.guide-panel blockquote {
  max-width: 68ch;
}

.guide-panel h4 {
  font-size: var(--text-lg);
  letter-spacing: var(--tracking-tight);
  margin-bottom: var(--space-4);
}

.guide-panel p + p {
  margin-top: var(--space-4);
}

/* The panel title is a column flex container (see .phase-panel__title above),
   so a bare badge would become a flex item and stretch to the full width of
   the panel. It is wrapped with the Phase's name in .phase-panel__name, which
   takes the line; the badge is then an ordinary inline-block beside the name
   and sizes to its own text. */
.phase-panel__name {
  display: block;
}

.phase-panel__badge,
.guide-badge {
  display: inline-block;
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-pill);
  color: var(--brand-strong);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0;
  padding: 0.15em var(--space-3);
  vertical-align: middle;
  white-space: nowrap;
}

.guide-definition {
  border-left: 3px solid var(--brand);
  color: var(--ink);
  font-size: var(--text-md);
  font-style: italic;
  line-height: var(--leading-normal);
  margin-top: var(--space-5);
  padding-left: var(--space-5);
}

.guide-goal {
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-md);
  margin-top: var(--space-5);
  padding: var(--space-4) var(--space-5);
}

.guide-goal__label {
  color: var(--brand-strong);
  font-weight: 800;
}

/* One group of material: a heading and everything under it. */
.guide-block {
  border-top: 1px solid var(--line);
  margin-top: var(--space-7);
  padding-top: var(--space-7);
}

.guide-numbered,
.guide-bullets {
  display: grid;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  padding-left: var(--space-5);
}

.guide-numbered {
  list-style: decimal;
}

.guide-bullets {
  list-style: disc;
}

.guide-numbered li,
.guide-bullets li {
  color: var(--ink-body);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  padding-left: var(--space-2);
}

.guide-numbered li::marker,
.guide-bullets li::marker {
  color: var(--brand);
  font-weight: 700;
}

/* ---- cards: quotes, avatars, stories, requirements, tests, bugs -------- */

.guide-voc,
.guide-avatars,
.guide-stories {
  display: grid;
  gap: var(--space-4);
  margin-top: var(--space-5);
}

@media (min-width: 901px) {
  .guide-voc,
  .guide-avatars {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.guide-voc__item,
.guide-avatar,
.guide-story,
.guide-req,
.guide-test,
.guide-bug {
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-5);
}

.guide-voc__item blockquote {
  color: var(--ink);
  font-size: var(--text-md);
  font-style: italic;
  line-height: var(--leading-snug);
}

.guide-voc__item figcaption {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  margin-top: var(--space-3);
}

.guide-avatar__name,
.guide-story__persona,
.guide-bug__title {
  color: var(--ink);
  font-size: var(--text-md);
  font-weight: 700;
  margin-bottom: var(--space-3);
}

.guide-story__voc,
.guide-req__id,
.guide-bug__id,
.guide-test__name {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

.guide-story__voc {
  color: var(--brand-strong);
  margin-top: var(--space-4);
}

.guide-req,
.guide-test,
.guide-bug {
  margin-top: var(--space-4);
}

.guide-req__id,
.guide-bug__id {
  color: var(--brand-strong);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  margin-bottom: var(--space-3);
  text-transform: uppercase;
}

.guide-test__name {
  color: var(--ink);
  font-size: var(--text-md);
  font-weight: 700;
  margin-bottom: var(--space-3);
}

.guide-test__why,
.guide-bug__lesson {
  border-top: 1px dashed var(--line-strong);
  color: var(--ink-muted);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
}

.guide-pattern {
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-md);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.6;
  padding: var(--space-4) var(--space-5);
}

/* ---- tables ------------------------------------------------------------ */

/* Wide content scrolls inside its own box; the page itself never does. */
.guide-table-scroll {
  margin-top: var(--space-5);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}

.guide-table {
  border-collapse: collapse;
  min-width: 34rem;
  width: 100%;
}

.guide-table th,
.guide-table td {
  border-bottom: 1px solid var(--line);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  padding: var(--space-3) var(--space-4);
  text-align: left;
  vertical-align: top;
}

.guide-table th {
  background: var(--surface-sunk);
  color: var(--ink-subtle);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
}

.guide-table td {
  color: var(--ink-body);
}

.guide-table tr:last-child td {
  border-bottom: 0;
}

.guide-table__id {
  color: var(--brand-strong);
  font-family: var(--font-mono);
  font-weight: 700;
  white-space: nowrap;
}

/* ---- flows and the state machine --------------------------------------- */

/* An indented flow is a diagram drawn in text: wrapping it destroys the
   structure, so it keeps its lines and scrolls sideways in its own box.
   tabindex in the markup makes that reachable without a pointer. */
.guide-flow {
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--ink-body);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.55;
  margin-top: var(--space-5);
  max-height: 30rem;
  overflow: auto;
  overscroll-behavior: contain;
  padding: var(--space-4);
  tab-size: 2;
  white-space: pre;
}

.guide-states {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  list-style: none;
  margin-top: var(--space-5);
  padding: 0;
}

/* One sequence rather than five badges, so the arrows sit on the panel's own
   surface. A tinted pill would put them on --brand-tint, where --ink-subtle is
   4.4:1 and would miss AA. */
.guide-states li {
  color: var(--brand-strong);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: 700;
  max-width: none;
}

.guide-states li + li::before {
  content: "→";
  color: var(--ink-subtle);
  font-weight: 400;
  margin-right: var(--space-3);
}

/* ---- the eval loop, docs to attach, and the files ---------------------- */

.guide-eval {
  border-top-color: var(--brand-edge);
}

.guide-docs {
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-left: 3px solid var(--brand-edge);
  border-radius: var(--radius-sm);
  color: var(--ink-body);
  font-size: var(--text-sm);
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
}

.guide-docs strong {
  color: var(--ink);
}

.guide-download {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  margin-top: var(--space-6);
  padding: var(--space-4) var(--space-5);
}

.guide-download__name {
  color: var(--ink);
  font-weight: 700;
}

.guide-download__note {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

/* An ordinary link to an ordinary file: it works with JavaScript blocked,
   which the frozen page's Blob-generated downloads did not. */
.guide-file {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  color: var(--ink-body);
  flex: none;
  font-size: var(--text-sm);
  font-weight: 700;
  padding: var(--space-2) var(--space-5);
  text-decoration: none;
  transition: border-color var(--transition), color var(--transition);
}

.guide-file::before {
  content: "\2193";
  font-weight: 700;
}

.guide-file:hover {
  border-color: var(--brand);
  color: var(--brand-strong);
}

/* The preview bounds a prompt box at 22rem so one Phase's prompt cannot push
   Home's other sections off the screen. The Guide is the page whose job is to
   be read end to end — the frozen live page showed every prompt whole — and a
   nested scroll region inside an already long page is a thing readers fight,
   so here the prompts run their full height. */
.guide-panel .prompt__text {
  max-height: none;
}

/* ---- back to the Phase list -------------------------------------------- */

/* The phone's Phase navigation. Hidden where the tablist sticks instead —
   and hidden again with JavaScript blocked, where there is no Phase list to
   go back to because all six Phases are already open. Same rule as the
   tablist and the copy control above it: no control that lies. */
html:not(.js) .guide-panel__back {
  display: none;
}

.guide-panel__back {
  display: none;
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}

.guide-panel__back a {
  color: var(--brand-strong);
  font-size: var(--text-sm);
  font-weight: 700;
}

/* ---- narrow screens ---------------------------------------------------- */

@media (max-width: 900px) {
  .guide-hero {
    padding-block: var(--space-8) var(--space-7);
  }

  .guide {
    padding-block: var(--space-7) var(--space-9);
  }

  .guide-panel {
    padding: var(--space-5) var(--space-4);
  }
}

@media (max-width: 700px) {
  html.js .guide-panel__back {
    display: block;
  }

  .guide-block {
    margin-top: var(--space-6);
    padding-top: var(--space-6);
  }

  .guide-voc__item,
  .guide-avatar,
  .guide-story,
  .guide-req,
  .guide-test,
  .guide-bug {
    padding: var(--space-4);
  }

  .guide-download {
    align-items: flex-start;
    flex-direction: column;
  }
}

@media (max-width: 560px) {
  .guide-flow {
    font-size: var(--text-xs);
    padding: var(--space-3);
  }
}

/* =========================================================================
   The thank-you page (#12)

   /thank-you is where formsubmit.co sends a visitor after a successful
   submission, and it is the only page on the site nothing links to: its
   inbound route is the _next field of the contact form. It carries the same
   header and footer as every other page — see the note in thank-you.html —
   and this is the one band between them.

   IT IS THE CLOSING BAND'S TREATMENT, ON PURPOSE. Same --brand-tint card, same
   border, same radius. A visitor who has read Home has already met this panel
   at the bottom of it, and meeting it again is what makes the page after a form
   submission feel like the same site rather than a redirect that went
   somewhere else. It is centred and narrower because it holds four short lines
   rather than a two-column band.

   THE BAND IS CENTRED IN THE VIEWPORT, NOT JUST ON THE PAGE. There is one
   section here and it is short — around 250px — so on a desktop window the
   natural layout leaves the band pinned under the header and the footer
   starting a third of the way down the screen. min-height takes the viewport
   less the header, which centres the band in what is left and keeps the footer
   out of the middle of the page. No position: fixed, and nothing overlaps on a
   short window, because it is a minimum rather than a height. Measured at
   1280x900: the band centres at ~430px and the footer begins at ~797px.

   Contrast on --brand-tint: --ink 16.2:1, --ink-body 11.6:1, --brand-strong
   7.4:1. --ink-subtle is 4.4:1 there and is NOT used on this surface.
   ========================================================================= */

.thanks {
  display: flex;
  align-items: center;
  min-height: calc(100vh - var(--header-height) - var(--space-11));
  padding-block: var(--space-10);
}

.thanks .container {
  width: 100%;
}

.thanks__band {
  max-width: 40rem;
  margin-inline: auto;
  background: var(--brand-tint);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-lg);
  padding: var(--space-8);
  text-align: center;
}

.thanks__mark {
  color: var(--brand-strong);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  margin-bottom: var(--space-4);
}

.thanks__title {
  font-size: clamp(1.875rem, 5vw, var(--text-3xl));
  margin-bottom: var(--space-4);
}

.thanks__lede {
  color: var(--ink-body);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  max-width: 46ch;
  margin-inline: auto;
}

/* The free material first and the purchase second, which is the reverse of the
   closing band on Home. Somebody who has just asked a question has not been
   sold to yet; the Guide is the answer to most of what they are likely to have
   asked. */
.thanks__actions {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin-top: var(--space-7);
}

.thanks__alt {
  display: inline-flex;
  align-items: center;
  color: var(--ink-body);
  font-size: var(--text-sm);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-color: var(--brand-edge);
  text-underline-offset: 0.32em;
  transition: color var(--transition), text-decoration-color var(--transition);
}

.thanks__alt:hover {
  color: var(--brand-strong);
  text-decoration-color: currentColor;
}

@media (max-width: 560px) {
  .thanks {
    min-height: 0;
    padding-block: var(--space-9);
  }

  .thanks__band {
    padding: var(--space-6) var(--space-5);
  }
}
