@charset "UTF-8";
/* ==========================================================================
   jayolivo.com
   site.css, the only stylesheet on the site.

   Hand written. No build step, no framework, no preprocessor.
   Page authors: you must not write new CSS and you must not use a style
   attribute. Everything you need is documented in docs/COMPONENTS.md.

   Contents, in order. Search for the two digit number to jump to a section.
     01 tokens light   02 tokens dark   03 reset        04 typography
     05 layout         06 utilities     07 header nav   08 buttons
     09 forms          10 cards         11 price        12 content blocks
     13 photo figure   14 cta band      15 footer       16 print
   ========================================================================== */

/* ==========================================================================
   01  TOKENS, LIGHT
   Brand tokens come from CLAUDE.md section 5. They are defined here on a bare
   :root so a color is never defined only inside a media query.
   The role tokens under them are derived from the brand tokens. They exist so
   a dark band can re-map colors for everything inside it without new rules.
   ========================================================================== */

:root {
  /* Brand, CLAUDE.md section 5 */
  --ink:      #16191c;  /* body text, near black */
  --paper:    #faf8f4;  /* page background, warm off white */
  --rust:     #b4522e;  /* single accent. buttons, rules */
  --rust-dk:  #8f3f22;  /* accent hover, and link text in light mode */
  --steel:    #4a5560;  /* secondary text, captions */
  --hairline: #e2ddd4;  /* borders, dividers */

  /* Derived surfaces and tints. Not new accents, just rust at low strength. */
  --paper-2:    #fffdfa;  /* raised panel on light */
  --rust-lt:    #e78f68;  /* rust that stays readable on a dark surface */
  --rust-lt-hv: #f3a984;  /* its hover */
  --rust-tint:  #f6ece7;  /* faint rust wash, placeholders and callouts */
  --rust-tint-2:#edd9cf;  /* the same wash, one step stronger */

  /* Role tokens. Components read these, never the brand tokens directly. */
  --surface:      var(--paper);
  --surface-2:    var(--paper-2);
  --fg:           var(--ink);
  --fg-muted:     var(--steel);
  --link:         var(--rust-dk);
  --link-hover:   var(--rust);
  --accent:       var(--rust);
  --accent-hover: var(--rust-dk);
  --on-accent:    #ffffff;
  --rule:         var(--hairline);
  --focus:        var(--rust-dk);
  --tint:         var(--rust-tint);
  --tint-2:       var(--rust-tint-2);

  /* The edge of a control, and only that. WCAG 1.4.11 wants 3:1 for the line
     that tells someone "this is a box you can type in", and --hairline is far
     too faint to carry that: it measures 1.33:1 on a field and 1.28:1 on the
     page. So inputs, selects, textareas, the ghost button and the Menu button
     use this instead. --rule stays for dividers, cards and table rows, where
     1.4.11 does not apply because nothing is being identified as a control.
     Measured: 3.47:1 on --paper, 3.63:1 on --paper-2, 3.17:1 on --tint. */
  --rule-strong: #8d8477;

  /* Error is a state color, not a second brand accent. It is used only for
     form validation and it is always paired with words, never color alone. */
  --error:      #9c2a17;
  --error-tint: #fbecea;

  /* The .band--ink palette. It lives out here, not inside the component, for
     two reasons. CLAUDE.md section 5 says a color is never defined only inside
     a media query, and the dark block in section 02 has to be able to move
     these, because a band painted #16191c on a #14171a page is invisible.
     The band carries its own error pair because the light mode one lands at
     2.32:1 on this surface, which nobody can read. */
  --ink-band:        #16191c;  /* the band surface */
  --ink-band-2:      #1e2226;  /* a raised card inside the band */
  --ink-band-fg:     #f4f1ec;
  --ink-band-muted:  #b6bcc3;
  --ink-band-rule:   #333a41;  /* dividers inside the band */
  --ink-band-strong: #7d8894;  /* control edges inside the band, see above */
  --ink-band-tint:   #22201e;
  --ink-band-tint-2: #2f2a26;
  --ink-band-error:      #f0938a;
  --ink-band-error-tint: #2a1715;

  /* Type. These two stacks are written out character for character in
     CLAUDE.md section 5. Do not add faces to them here without changing it
     there first, or the file and the law stop saying the same thing. */
  --font-display: Newsreader, Georgia, serif;
  --font-text:    Inter, system-ui, -apple-system, "Segoe UI", sans-serif;

  --step--1: clamp(0.875rem, 0.85rem + 0.12vw, 0.9375rem);
  --step-0:  clamp(1.0625rem, 1.03rem + 0.18vw, 1.125rem);
  --step-1:  clamp(1.25rem, 1.18rem + 0.35vw, 1.4375rem);
  --step-2:  clamp(1.5rem, 1.36rem + 0.7vw, 1.9375rem);
  --step-3:  clamp(1.875rem, 1.6rem + 1.35vw, 2.625rem);
  --step-4:  clamp(2.25rem, 1.8rem + 2.2vw, 3.375rem);

  --measure: 68ch;   /* max line length, CLAUDE.md section 5 */
  --wrap:    68rem;  /* page container */

  --gap:   clamp(1rem, 0.7rem + 1.4vw, 1.75rem);
  --band:  clamp(2.75rem, 1.8rem + 4.2vw, 5.25rem);
  --radius: 4px;

  color-scheme: light;
}

/* ==========================================================================
   02  TOKENS, DARK
   Only the values change. Every color already exists on :root above.
   Measured contrast on the dark palette: body text 14.6:1, secondary text
   8.2:1, link text 7.8:1 on the page and 7.1:1 on a raised card, button
   labels 8.0:1. All AAA.
   ========================================================================== */

@media (prefers-color-scheme: dark) {
  :root {
    --ink:      #ece7df;  /* now the light text on a dark page */
    --paper:    #14171a;  /* now the dark page background */
    --rust:     #ea9573;  /* lifted so link text clears AAA on a raised card */
    --rust-dk:  #f4ab8a;  /* on a dark page the hover goes brighter, not darker */
    --steel:    #a8b0b8;
    --hairline: #2c3238;

    --paper-2:    #1c2025;
    --rust-lt:    #e78f68;
    --rust-lt-hv: #f3a984;
    --rust-tint:  #241a15;
    --rust-tint-2:#33241c;

    --link:         var(--rust);
    --link-hover:   var(--rust-dk);
    --accent:       var(--rust);
    --accent-hover: var(--rust-dk);
    --on-accent:    #17120f;
    --focus:        var(--rust-dk);

    --error:      #f0938a;
    --error-tint: #2a1715;

    --rule-strong: #69737e;  /* 3.73:1 on the page, 3.39:1 on a field */

    /* The ink band goes LIGHTER than the page in dark mode, not darker. At its
       light mode value it measured 1.02:1 against this page background, which
       is no visible band at all. Measured on #262c32: body text 12.52:1,
       muted 7.37:1, link 5.73:1, error 6.21:1. The link is the one number that
       is AA and not AAA, and it is the price of a band you can actually see.
       Darkening this again to buy that back brings the invisible band back. */
    --ink-band:        #262c32;
    --ink-band-2:      #313841;
    --ink-band-rule:   #3a424a;
    --ink-band-tint:   #38312a;
    --ink-band-tint-2: #453c33;

    color-scheme: dark;
  }
}

/* ==========================================================================
   03  RESET
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body,
h1, h2, h3, h4, h5, h6,
p, figure, blockquote, dl, dd, ol, ul, fieldset, legend { margin: 0; }
ul[class], ol[class] { padding: 0; list-style: none; }
img, picture, video, svg { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; }
table { border-collapse: collapse; width: 100%; }
fieldset { border: 0; padding: 0; min-width: 0; }
hr { border: 0; border-top: 1px solid var(--rule); margin-block: var(--gap); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   04  BASE TYPOGRAPHY
   ========================================================================== */

body {
  background: var(--surface);
  color: var(--fg);
  font-family: var(--font-text);
  font-size: var(--step-0);
  font-weight: 400;
  line-height: 1.65;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

main { flex: 1 0 auto; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-wrap: balance;
  color: var(--fg);
}

h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }

h5, h6 {
  font-family: var(--font-text);
  font-size: var(--step-0);
  font-weight: 600;
  line-height: 1.35;
  margin: 0;
}

p { text-wrap: pretty; }
a { color: var(--link); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--link-hover); }
strong, b { font-weight: 600; }
small { font-size: var(--step--1); }

blockquote {
  border-left: 3px solid var(--accent);
  padding-left: var(--gap);
  font-family: var(--font-display);
  font-size: var(--step-1);
  line-height: 1.45;
}

blockquote cite {
  display: block;
  margin-top: 0.6em;
  font-family: var(--font-text);
  font-size: var(--step--1);
  font-style: normal;
  color: var(--fg-muted);
}

code, pre, kbd {
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: 0.92em;
}

::selection { background: var(--tint-2); color: var(--fg); }

/* One focus treatment for the whole site. Never removed anywhere. */
:where(a, button, input, select, textarea, summary, label, [tabindex]):focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ==========================================================================
   05  LAYOUT, WRAP, PROSE, BAND
   ========================================================================== */

.wrap { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }
.wrap--narrow { --wrap: 46rem; }

/* .prose is the reading column. It caps the measure at 68 characters and it
   sets the vertical rhythm for plain markup, so page authors write normal
   paragraphs and headings without touching a class on every element. */
.prose { max-width: var(--measure); }
.prose > * + * { margin-top: 1.1em; }
.prose > * + h2 { margin-top: 2.2em; }
.prose > * + h3 { margin-top: 1.8em; }
.prose > h2 + *,
.prose > h3 + * { margin-top: 0.7em; }
.prose > ul, .prose > ol { padding-left: 1.25rem; }
.prose > ul > li + li,
.prose > ol > li + li { margin-top: 0.4em; }
.prose ::marker { color: var(--accent); }
.prose--center { margin-inline: auto; }

/* Vertical rhythm. Every major section on a page is a .band. */
.band { padding-block: var(--band); }
.band--tight { padding-block: calc(var(--band) * 0.55); }
.band--paper { background: var(--surface-2); border-block: 1px solid var(--rule); }

/* A dark band. It re-maps the role tokens so anything nested inside it,
   including links, buttons and a form in an error state, stays readable with
   no extra classes. Every colour that has to change between light and dark
   comes from an --ink-band-* token in section 01, because a literal hex
   written in here cannot be reached by a media query, which is exactly how
   this band used to disappear in dark mode. The one hex left in here,
   --on-accent, is the same value in both themes.
   Re-map every role token a component reads. If you add one to :root, add it
   here too, or it silently keeps its page value inside the band. */
.band--ink {
  --surface:   var(--ink-band);
  --surface-2: var(--ink-band-2);
  --fg:        var(--ink-band-fg);
  --fg-muted:  var(--ink-band-muted);
  --link:      var(--rust-lt);
  --link-hover:var(--rust-lt-hv);
  --accent:    var(--rust-lt);
  --accent-hover: var(--rust-lt-hv);
  --on-accent: #17120f;
  --rule:      var(--ink-band-rule);
  --rule-strong: var(--ink-band-strong);
  --focus:     var(--rust-lt-hv);
  --tint:      var(--ink-band-tint);
  --tint-2:    var(--ink-band-tint-2);
  /* Without these two, a rejected field inside this band inherits the page's
     light mode error colors: 2.32:1 for the message and 1.02:1 for the text
     the person typed. Both effectively invisible. */
  --error:      var(--ink-band-error);
  --error-tint: var(--ink-band-error-tint);
  background: var(--surface);
  color: var(--fg);
  /* The edge is drawn even when the band and the page sit close together. */
  border-block: 1px solid var(--rule);
}

/* Two neighbours that both draw an edge would draw a 2px line between them. */
.band + .band--paper,
.band--paper + .band--paper,
.band--paper + .band--ink,
.band--ink + .band--ink { border-top: 0; }

/* Simple, honest grids. They collapse to one column on small screens. */
.grid { display: grid; gap: var(--gap); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); }

/* Two column text and sidebar. Stacks under 52em. */
.split { display: grid; gap: var(--gap); }

@media (min-width: 52em) {
  .split { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); align-items: start; }
}

.stack > * + * { margin-top: var(--gap); }
.stack--tight > * + * { margin-top: 0.6rem; }

/* Hero. One primary action, per CLAUDE.md section 1. */
.hero { padding-block: clamp(3rem, 2rem + 5vw, 6rem); }
.hero .eyebrow { margin-bottom: 0.75rem; }
.hero-title { max-width: 20ch; }
.hero-lede {
  margin-top: 1.1rem;
  max-width: 54ch;
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--fg-muted);
}
.hero-actions { margin-top: 2rem; }

.eyebrow {
  font-family: var(--font-text);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
}

.lede { font-size: var(--step-1); line-height: 1.5; color: var(--fg-muted); max-width: 56ch; }
.section-title { max-width: 24ch; }
.meta { font-size: var(--step--1); color: var(--fg-muted); }
.list-plain { list-style: none; padding: 0; }
.list-plain > li + li { margin-top: 0.6rem; }
.list-rule { list-style: none; padding: 0; }
.list-rule > li { padding-block: 0.85rem; border-bottom: 1px solid var(--rule); }
.list-rule > li:first-child { border-top: 1px solid var(--rule); }

.tag {
  display: inline-block;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--fg-muted);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.15em 0.7em;
  line-height: 1.5;
}

/* ==========================================================================
   06  SKIP LINK AND UTILITIES
   ========================================================================== */

.skip-link {
  position: absolute;
  left: 0.75rem;
  top: -100%;
  z-index: 100;
  background: var(--accent);
  color: var(--on-accent);
  padding: 0.75rem 1.1rem;
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--radius);
}
.skip-link:focus { top: 0.75rem; color: var(--on-accent); }

.visually-hidden:not(:focus):not(:active) {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Honeypot field on forms. Never shown, never focusable. */
.hp { display: none; }
.center { text-align: center; }
.center-block { margin-inline: auto; }
.muted { color: var(--fg-muted); }
.nowrap { white-space: nowrap; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }

/* ==========================================================================
   07  HEADER AND NAV
   The mobile nav works with JavaScript off. It is a checkbox plus a label.
   CSS alone opens and closes it. site.js upgrades it to a real button with
   aria-expanded, Escape to close, and close on outside click. If the script
   never loads, the checkbox still works.
   ========================================================================== */

.site-header {
  border-bottom: 1px solid var(--rule);
  background: var(--surface);
  position: relative;
  z-index: 20;
}
.site-header .wrap {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  min-height: 4.25rem;
  padding-block: 0.65rem;
}

.brand {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--step-1);
  line-height: 1.1;
  color: var(--fg);
  text-decoration: none;
  margin-right: auto;
  padding-block: 0.35rem;
}
.brand:hover { color: var(--accent); }
.brand small {
  display: block;
  font-family: var(--font-text);
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-top: 0.15rem;
}

/* The checkbox holds the open state. It is hidden from sight but still
   focusable, and its focus ring is drawn on the label next to it. */
.nav-checkbox {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.nav-button {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
  padding: 0.5rem 0.85rem;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  font-size: var(--step--1);
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  color: var(--fg);
}
.nav-button:hover { border-color: var(--accent); color: var(--accent); }
.nav-checkbox:focus-visible + .nav-button { outline: 3px solid var(--focus); outline-offset: 2px; }

/* Three bars, drawn in CSS so there is no icon font and no SVG request. */
.nav-bars {
  position: relative;
  display: block;
  width: 18px;
  height: 2px;
  background: currentColor;
}

.nav-bars::before,
.nav-bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 18px;
  height: 2px;
  background: currentColor;
}
.nav-bars::before { top: -6px; }
.nav-bars::after  { top: 6px; }
.site-nav { display: none; flex-basis: 100%; padding-top: 0.5rem; }
.nav-checkbox:checked ~ .site-nav { display: block; }
.nav-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}
.nav-link {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 0.25rem;
  color: var(--fg);
  text-decoration: none;
  font-weight: 400;
  border-bottom: 1px solid var(--rule);
}
.nav-link:hover { color: var(--accent); }
.nav-link[aria-current="page"] { color: var(--accent); font-weight: 600; }

/* The one secondary action in the header. Never a second primary button.
   It sits after the menu in the markup and in the layout at every width, so
   reading order and tab order always match. On a phone with the menu closed it
   sits beside the Menu button. With the menu open it becomes the last row of
   the menu, which is a fair place for the second door. */
.nav-cta {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 1rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  color: var(--accent);
  font-weight: 600;
  font-size: var(--step--1);
  text-decoration: none;
}
.nav-cta:hover { background: var(--tint); color: var(--accent-hover); border-color: var(--accent-hover); }

/* 60em, not 52em. The desktop row does not actually fit until about 875px:
   at 834px, which is an iPad Pro in landscape, the brand tagline and two of
   the nav links each broke onto a second line and the header grew from 68px
   to 89px. Do not lower this number. The .split breakpoint further up is a
   different question and it is still 52em on purpose.
   If site.js is ever edited, its stale-menu matchMedia has to match 60em too. */
@media (min-width: 60em) {
  .nav-button, .nav-checkbox { display: none; }
  .site-nav { display: block; flex-basis: auto; padding-top: 0; }
  .nav-list { flex-direction: row; align-items: center; gap: 1.6rem; }
  .nav-link { border-bottom: 0; padding: 0.25rem 0; min-height: 0; }
  .nav-link[aria-current="page"] { box-shadow: inset 0 -2px 0 0 var(--accent); }
  .nav-cta { margin-left: 0.6rem; }
  .site-header .wrap { flex-wrap: nowrap; }
  /* Belt and braces on the row above: it can never silently wrap again.
     These two live inside the media query on purpose. The tagline is wider
     than a 320px phone, so nowrap at every width would push the page sideways
     instead, which is the worse of the two problems. */
  .nav-link { white-space: nowrap; }
  .brand small { white-space: nowrap; }
}

/* ==========================================================================
   08  BUTTONS
   .btn is primary and there is one per page. .btn-ghost is everything else.
   ========================================================================== */

.btn,
.btn-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 44px;
  padding: 0.7rem 1.4rem;
  border-radius: var(--radius);
  font-family: var(--font-text);
  font-size: var(--step-0);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  border: 2px solid transparent;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.btn { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: var(--on-accent);
}
.btn:active { transform: translateY(1px); }
/* The border is the only thing saying this is a control, so it uses the
   stronger edge token. The hover colour is --accent-hover, not --accent:
   --accent on --tint measures 4.32:1 in light mode, which is under 1.4.3, and
   this label is 17px to 18px so it does not get the large text allowance.
   --accent-hover measures 6.23:1 light, 8.95:1 dark, 8.37:1 in an ink band. */
.btn-ghost { background: transparent; border-color: var(--rule-strong); color: var(--fg); }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent-hover); background: var(--tint); }
.btn-ghost:active { transform: translateY(1px); }

.btn[disabled], .btn-ghost[disabled],
.btn[aria-disabled="true"], .btn-ghost[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

.btn--wide, .btn-ghost--wide { width: 100%; }
.btn-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.9rem 1.25rem; }

/* A quiet text link that still reads as an action. */
.link-arrow {
  font-weight: 600;
  text-decoration: none;
  border-bottom: 2px solid var(--tint-2);
  padding-bottom: 1px;
}
.link-arrow:hover { border-bottom-color: var(--accent); }
.link-arrow::after { content: " \203A"; }

/* ==========================================================================
   09  FORMS
   ========================================================================== */

.form { max-width: 34rem; }
.form--wide { max-width: 48rem; }
.field { margin-bottom: 1.35rem; }

.label {
  display: block;
  font-weight: 600;
  font-size: var(--step--1);
  margin-bottom: 0.35rem;
  color: var(--fg);
}
.label .req { color: var(--accent); font-weight: 600; }

.input, .textarea, .select {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 0.65rem 0.8rem;
  background: var(--surface-2);
  color: var(--fg);
  /* --rule-strong, not --rule. The fill of a field is almost the same colour
     as the page behind it, 1.04:1 in light mode, so the border is the only
     thing that says a field is there. WCAG 1.4.11 wants that line at 3:1. */
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  font-size: var(--step-0);
  line-height: 1.45;
}

.textarea { min-height: 9rem; resize: vertical; }

.select {
  appearance: none;
  padding-right: 2.4rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 1.05rem center, right 0.75rem center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

.input:hover, .textarea:hover, .select:hover { border-color: var(--fg-muted); }
.input:focus, .textarea:focus, .select:focus {
  border-color: var(--accent);
  outline: 3px solid var(--focus);
  outline-offset: 1px;
}
/* No opacity here. Opacity blends the text into the field fill, and at 0.8 the
   result measured 4.49:1, just under the 4.5:1 placeholder text needs. The
   colour alone is 7.50:1 light and 7.46:1 dark. If a placeholder ever needs to
   sit back further than this, the answer is its own token, not opacity. */
.input::placeholder, .textarea::placeholder { color: var(--fg-muted); }
.help { display: block; margin-top: 0.4rem; font-size: var(--step--1); color: var(--fg-muted); }

/* Checkbox and radio. The control keeps a 44px target through the label. */
.check, .radio {
  display: grid;
  grid-template-columns: 1.25rem 1fr;
  gap: 0.7rem;
  align-items: start;
  min-height: 44px;
  padding-block: 0.6rem;
  cursor: pointer;
  font-size: var(--step-0);
}
.check + .check, .radio + .radio { margin-top: 0.1rem; }
.check input, .radio input {
  width: 1.25rem;
  height: 1.25rem;
  margin: 0.22rem 0 0 0;
  accent-color: var(--accent);
  cursor: pointer;
}

.fieldset { margin-bottom: 1.5rem; }
.fieldset > legend {
  font-weight: 600;
  font-size: var(--step--1);
  padding: 0;
  margin-bottom: 0.35rem;
}

/* The legend when it holds the question itself and not a short label.
   On /lbl10 the legend IS the ten statements, and at --step--1 each one
   measured 14.05px on a 375px screen while the five answers under it measured
   17.16px. The questions were the smallest text on a page that is nothing but
   questions. This puts the statement at body size and leaves the .eyebrow
   inside it small, because that is a label and not the question.
   Deliberately a modifier and not a change to the rule above: the legends on
   /apply and /letter are short labels and they read correctly at 14px. */
.fieldset--statement > legend { font-size: var(--step-0); line-height: 1.4; }

/* Error state. Driven by .is-error on the .field plus aria-invalid on the
   control. Never color alone. The message is always words. */
.field.is-error .label { color: var(--error); }

/* A radio or checkbox group lives in a .fieldset, not a .field, so it is the
   legend that turns, not a label. site.js marks the fieldset the same way. */
.fieldset.is-error > legend { color: var(--error); }

.field.is-error .input,
.field.is-error .textarea,
.field.is-error .select,
.input[aria-invalid="true"],
.textarea[aria-invalid="true"],
.select[aria-invalid="true"] {
  border-color: var(--error);
  border-width: 2px;
  background: var(--error-tint);
}

.error {
  display: block;
  margin-top: 0.4rem;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--error);
}
.error::before { content: "Error. "; }
.error:empty { display: none; }

/* Browser side validation, only after the person has actually interacted. */
.input:user-invalid,
.textarea:user-invalid,
.select:user-invalid { border-color: var(--error); border-width: 2px; }

.input:-moz-ui-invalid,
.textarea:-moz-ui-invalid,
.select:-moz-ui-invalid { border-color: var(--error); border-width: 2px; }

/* A whole form message, used by subscribe.php and apply.php on reload. */
.form-message {
  padding: 0.9rem 1.1rem;
  border-left: 4px solid var(--accent);
  background: var(--tint);
  border-radius: 0 var(--radius) var(--radius) 0;
  margin-bottom: 1.35rem;
}

.form-message--error { border-left-color: var(--error); background: var(--error-tint); }
.form-message > :first-child { margin-top: 0; }

/* The short email form used inside the CTA band. */
.subscribe-form { display: flex; flex-wrap: wrap; gap: 0.75rem; max-width: 34rem; }
/* Direct child only. In the skeleton the email input sits inside a .field, so
   the input is not a flex item and this flex value would be ignored, while
   width: auto still cancelled the width: 100% above and left the field about
   100px short of the button. The .field is the flex item, two lines down. */
.subscribe-form > .input { flex: 1 1 15rem; width: auto; }
.subscribe-form .btn { flex: 0 0 auto; }
.subscribe-form .field { margin-bottom: 0; flex: 1 1 15rem; }
.subscribe-form .help,
.subscribe-form .error { flex-basis: 100%; margin-top: 0; }

/* ==========================================================================
   10  CARDS, LETTER CARDS, PLACEHOLDERS
   ========================================================================== */

.card {
  display: flex;
  flex-direction: column;
  padding: clamp(1.15rem, 0.9rem + 1vw, 1.75rem);
  background: var(--surface-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.card > * + * { margin-top: 0.75rem; }
.card-title { font-size: var(--step-1); margin: 0; }
.card-title a { text-decoration: none; color: var(--fg); }
.card-title a:hover { color: var(--accent); }
.card-meta { font-size: var(--step--1); color: var(--fg-muted); margin: 0; }
.card-body { margin: 0; }
.card-body p + p { margin-top: 0.75rem; }
.card-foot { margin-top: auto; padding-top: 1rem; }

/* Letter archive card. Used on the home page and on letter.html. */
.letter-card { border-left: 3px solid var(--accent); }
.letter-card .card-meta {
  font-family: var(--font-text);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 600;
}
.letter-card .card-title { font-size: var(--step-2); line-height: 1.2; }
.letter-list { display: grid; gap: var(--gap); }

/* Placeholder. This must be impossible to mistake for real content.
   Dashed rust border, a tinted striped background, and a visible label.
   docs/DEPLOY.md carries the list of every place one of these lives, and each
   page adds itself to that list as it is built. Delete the whole element to
   remove one. Nothing else needs to change. */
.placeholder {
  position: relative;
  border: 2px dashed var(--accent);
  border-left-width: 2px;
  background-color: var(--tint);
  background-image: repeating-linear-gradient(
    135deg,
    transparent 0 12px,
    var(--tint-2) 12px 14px
  );
}

.placeholder__label {
  display: inline-block;
  align-self: flex-start;
  margin: 0 0 0.35rem 0;
  padding: 0.25em 0.7em;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border-radius: 2px;
}

.placeholder .card-title,
.placeholder .card-body,
.placeholder .card-meta { color: var(--fg-muted); }
.placeholder .card-title { font-style: italic; }

/* ==========================================================================
   11  PRICE BLOCK
   Plain and calm. No badges, no strikethrough, no urgency. See
   docs/BUILD-DECISIONS.md section 1. Do not add one.
   ========================================================================== */

.price-block {
  border: 1px solid var(--rule);
  border-top: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface-2);
  padding: clamp(1.25rem, 1rem + 1.2vw, 2rem);
  max-width: 34rem;
}
.price-row + .price-row {
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--rule);
}
.price-label {
  display: block;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-bottom: 0.15rem;
}
.price-amount {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--step-3);
  line-height: 1.1;
  color: var(--fg);
}
.price-amount .price-term {
  font-family: var(--font-text);
  font-size: var(--step-0);
  font-weight: 400;
  color: var(--fg-muted);
}
.price-note {
  margin-top: 0.45rem;
  font-size: var(--step--1);
  color: var(--fg-muted);
  max-width: 46ch;
}
.price-why { margin-top: 1.4rem; padding-top: 1.2rem; border-top: 1px solid var(--rule); }
.price-why > :first-child { margin-top: 0; }

/* ==========================================================================
   12  CALLOUT, NOTE, DEFINITION LIST, TABLE
   ========================================================================== */

.callout {
  border-left: 4px solid var(--accent);
  background: var(--tint);
  padding: clamp(1rem, 0.85rem + 0.7vw, 1.5rem);
  border-radius: 0 var(--radius) var(--radius) 0;
  max-width: var(--measure);
}
.callout > :first-child { margin-top: 0; }
.callout > * + * { margin-top: 0.8rem; }
.callout-title {
  font-family: var(--font-text);
  font-size: var(--step-0);
  font-weight: 600;
  margin: 0;
}

.note {
  font-size: var(--step--1);
  color: var(--fg-muted);
  border-top: 1px solid var(--rule);
  padding-top: 0.8rem;
  max-width: var(--measure);
}

.dl { margin: 0; max-width: var(--measure); }
.dl dt {
  font-weight: 600;
  font-size: var(--step--1);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.dl dd { margin: 0.25rem 0 0 0; padding-bottom: 0.9rem; border-bottom: 1px solid var(--rule); }
.dl dd + dt { margin-top: 0.9rem; }

@media (min-width: 40em) {
  .dl--rows { display: grid; grid-template-columns: minmax(8rem, 14rem) 1fr; column-gap: var(--gap); }
  .dl--rows dt { padding-top: 0.9rem; border-top: 1px solid var(--rule); }
  .dl--rows dd { margin-top: 0; padding-top: 0.9rem; padding-bottom: 0.9rem; border-top: 1px solid var(--rule); border-bottom: 0; }
  .dl--rows dd + dt { margin-top: 0; }
}

/* Tables scroll sideways on a phone instead of breaking the page. */
.table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.table { width: 100%; font-size: var(--step--1); background: var(--surface-2); }
.table th, .table td {
  text-align: left;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}
.table thead th {
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  font-size: 0.75rem;
  color: var(--fg-muted);
  background: var(--surface);
}
.table tbody tr:last-child td { border-bottom: 0; }
.table caption {
  caption-side: bottom;
  padding: 0.7rem 0.9rem;
  font-size: var(--step--1);
  color: var(--fg-muted);
  text-align: left;
}

/* ==========================================================================
   13  OPTIONAL PHOTO FIGURE
   Read docs/BUILD-DECISIONS.md section 3. There is ONE photo on this site and
   it may never arrive. So there is no reserved box here, no min-height, no
   background, no aspect ratio holder, and no border on the container.
   If there is no photo, page authors delete the whole <figure> element and the
   page reflows to a finished layout. Nothing collapses and nothing is empty.
   ========================================================================== */

.figure { margin: 0; }
.figure:empty { display: none; }
.figure img { width: 100%; border-radius: var(--radius); border: 1px solid var(--rule); }
.figure figcaption {
  margin-top: 0.55rem;
  font-size: var(--step--1);
  color: var(--fg-muted);
  max-width: 46ch;
}

/* Optional side placement at wide widths. Still no reserved space: this only
   does anything when a figure element is actually present in the markup. */
@media (min-width: 60em) {
  .figure--side { float: right; width: 18rem; margin: 0.35rem 0 1.25rem 2rem; }
}

/* ==========================================================================
   14  CTA BAND
   The shared subscribe block at the end of every page. One action.
   ========================================================================== */

.cta {
  border-top: 1px solid var(--rule);
  background: var(--surface-2);
  padding-block: var(--band);
}
.cta-inner { max-width: 44rem; }
.cta-title { font-size: var(--step-2); margin: 0; max-width: 22ch; }
.cta-text { margin-top: 0.8rem; color: var(--fg-muted); max-width: 52ch; }
.cta .subscribe-form { margin-top: 1.5rem; }
.cta-fineprint {
  margin-top: 1rem;
  font-size: var(--step--1);
  color: var(--fg-muted);
  max-width: 52ch;
}

/* .cta--quiet
   For a page whose OWN primary action is not subscribing. /the-room asks you to
   apply, /apply asks you to submit. On those pages the closing subscribe block
   still has to be there, because CLAUDE.md section 1 puts it at the end of every
   page, but it must not compete with the button that page is actually for.
   CLAUDE.md section 1 also says a page with two equally weighted buttons is
   wrong, and without this modifier every one of those pages would have two.
   The markup change is one token: class="cta cta--quiet". Nothing else moves. */
.cta--quiet { background: transparent; }
.cta--quiet .cta-title { font-size: var(--step-1); }
.cta--quiet .subscribe-form .btn {
  background: transparent;
  border-color: var(--rule-strong);
  color: var(--fg);
}
.cta--quiet .subscribe-form .btn:hover {
  border-color: var(--accent);
  color: var(--accent-hover);
  background: var(--tint);
}

/* ==========================================================================
   15  FOOTER
   ========================================================================== */

.site-footer {
  border-top: 1px solid var(--rule);
  background: var(--surface);
  padding-block: var(--band) 2rem;
  font-size: var(--step--1);
}

.footer-cols {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
}
.footer-heading {
  font-family: var(--font-text);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin: 0 0 0.7rem 0;
}
.footer-nav { list-style: none; margin: 0; padding: 0; }
.footer-nav li + li { margin-top: 0.5rem; }
.footer-nav a {
  color: var(--fg);
  text-decoration: none;
  display: inline-block;
  padding-block: 0.15rem;
}
.footer-nav a:hover { color: var(--accent); text-decoration: underline; }
.footer-note { color: var(--fg-muted); max-width: 40ch; }
.footer-note > * + * { margin-top: 0.6rem; }
.footer-meta {
  margin-top: var(--gap);
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.5rem;
  color: var(--fg-muted);
}
.footer-meta p { margin: 0; }

/* ==========================================================================
   16  PRINT
   ========================================================================== */

@media print {
  :root {
    --surface: #ffffff;
    --surface-2: #ffffff;
    --fg: #000000;
    --fg-muted: #333333;
    --rule: #bbbbbb;
    --rule-strong: #999999;
    --tint: #ffffff;
    --tint-2: #ffffff;
  }

  /* The ink band has to be flattened through its own tokens. Its --fg wins
     over the one above for anything inside it, and a heading sets its colour
     explicitly, so without this a heading in an ink band prints white on
     white. The background rule further down cannot reach it. */
  .band--ink {
    --ink-band: #ffffff;
    --ink-band-2: #ffffff;
    --ink-band-fg: #000000;
    --ink-band-muted: #333333;
    --ink-band-rule: #bbbbbb;
    --ink-band-strong: #999999;
    --ink-band-tint: #ffffff;
    --ink-band-tint-2: #ffffff;
  }

  body { background: #fff; color: #000; font-size: 11pt; line-height: 1.45; }

  .site-header, .site-footer, .cta, .skip-link,
  .nav-button, .nav-checkbox, .btn, .btn-ghost, .subscribe-form { display: none !important; }

  .band, .band--paper, .band--ink {
    background: #fff !important;
    color: #000 !important;
    border: 0;
    padding-block: 0.6rem;
  }

  .wrap { width: 100%; }
  .prose { max-width: none; }
  a { color: #000; text-decoration: underline; }
  .prose a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; word-break: break-all; }
  .card, .price-block, .callout, .table-wrap { border: 1px solid #999; break-inside: avoid; }
  .placeholder { border: 2px dashed #999; background: #fff; }
  h1, h2, h3 { break-after: avoid; }
  img, figure, table { break-inside: avoid; }
}
