/* ==========================================================================
   thecolleenlawson.com
   Design system extracted from "Homepage LOCKED B - Band portrait".
   Mobile first. The main breakpoint is 1000px (max-width 999px and
   min-width 1000px), plus a few narrower steps and print. DESIGN.md at the
   repo root records the finished system.
   ========================================================================== */

:root {
  /* --- Palette ---------------------------------------------------------
     Four values are deliberately darker than the mockup, for contrast.
     Every ratio below is measured against the background it sits on.      */
  --bg:          #f6f5f1;
  --bg-panel:    #ffffff;

  --ink:         #121417;   /* headings and body                          */
  --ink-soft:    #3a4041;   /* about, field labels                 9.68:1  */
  --ink-muted:   #666b6a;   /* chip hover, small print on cream    4.97:1  */
  --ink-card:    #585d5c;   /* fee card body                      6.14:1  */

  /* The brand colour is forest green, chosen by Colleen on 2026-11-11
     (forest + lilac, over slate + mint and teal + hot pink). It is also
     the field: see "Field edition" below for the field and the accent.
     It is also the price colour (see .fee-price). */
  --green:       #163a2b;   /* 11.50:1 on --bg, 12.54:1 reversed out      */
  --green-hover: #0d231a;

  --rule-quote:  #d8d4ca;   /* divider between advisor quotes             */
  --rule-card:   #bdb7a9;
  --rule-input:  #8c8981;   /* was #b9b6ae (2.03:1)  ->  3.49:1, WCAG 1.4.11 */

  --ph-a:        #eae7df;   /* placeholder hatch, light stripe            */
  --ph-b:        #e2dfd6;   /* placeholder hatch, dark stripe             */
  --ph-ink:      #5a5f5b;   /* was #666b65 (4.07:1)  ->  4.89:1 on --ph-b */

  --err-on-field:#ffab96;   /* failed send and field errors, on the field 6.88:1 */

  /* --- Type ----------------------------------------------------------- */
  --font: Helvetica, 'Helvetica Neue', Arial, sans-serif;
  --mono: ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;

  /* --- Layout --------------------------------------------------------- */
  --maxw:  1320px;
  --pad-x: clamp(20px, 6vw, 80px);
}

/* --- Reset ------------------------------------------------------------ */

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

html,
body { margin: 0; padding: 0; }

body {
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  -webkit-text-size-adjust: 100%;
}

a { color: var(--green); }
a:hover { color: var(--green-hover); }

/* Shop terms (CDE, LDE, PBIS, CATA) keep the surrounding text style; the
   spelled-out name shows on hover, and the help cursor hints that it will.
   Only the first use of a term that a reader outside FFA might not know
   gets one. FFA itself is the organization's name, not an abbreviation. */
abbr[title] { text-decoration: none; cursor: help; }

/* Scoped to links and buttons. Form controls are excluded deliberately: their
   focus is drawn around the surrounding field box instead (see
   .field:focus-within), which would otherwise be doubled by a ring. */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

.field-input:focus,
.field-input:focus-visible { outline: none; }

/* Skip-link target. The scroll itself is the feedback; a ring on the whole
   <main> would be noise. */
main:focus { outline: none; }
/* The form messages take focus after a send so screen readers announce
   them (form.js, and :target without it). They are not controls, so a
   focus ring around one reads as a stray box. */
.form-status:focus { outline: none; }

.skip-link {
  position: absolute;
  top: 8px;
  left: -9999px;
  z-index: 10;
  background: var(--green);
  color: #fff;
  padding: 12px 20px;
  font-size: 16px;
  font-weight: 700;
  text-decoration: none;
}
.skip-link:focus { left: 8px; }

/* --- Shared shells ---------------------------------------------------- */

.panel { background: var(--bg-panel); }

.btn {
  display: inline-block;
  background: var(--green);
  color: #fff;
  text-decoration: none;
  padding: 17px 32px;
  font: 700 16px var(--font);
  border: none;
  cursor: pointer;
}
.btn:hover,
.btn:active {
  background: var(--green-hover);
  color: #fff;
}
/* A send button while a send is in flight (form.js). Only the contact
   buttons ever go busy, so the fill is theirs: see .contact .btn:disabled. */
.btn:disabled { cursor: progress; }

/* Motion is limited to colour fades, a nudge on the arrow, and the floating
   button on phones easing in and out, and only for visitors who have not
   asked for reduced motion. Nothing animates on load: the page is complete
   the moment it renders. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
  .btn,
  .link-arrow,
  .link-arrow-text { transition: background-color .15s, color .15s, border-color .15s; }
  .link-arrow-mark { transition: transform .15s; }
}

/* Placeholder chrome for a pending image or video: the hatched grey box and
   its label. Replaced wholesale when the real asset arrives. */
.ph-box {
  background: repeating-linear-gradient(135deg,
    var(--ph-a) 0 10px, var(--ph-b) 10px 20px);
}
.ph-label {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .08em;
  color: var(--ph-ink);
}

/* Marks stand-in content. Carries no typography of its own: specimen copy is
   shown in the final styles so the real text can be judged in place, and the
   bracketed strings are what signal it is not written yet. The class is what
   the print stylesheet hides and what the empty-band test below keys off, so
   it must come off the element at the same time the real copy goes in. */
.specimen {}

/* --- Header ----------------------------------------------------------- */

.site-header {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 15px var(--pad-x);
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: space-between;
}

.site-name {
  font-size: clamp(13px, 1.15vw, 17px);
  font-weight: 700;
  letter-spacing: clamp(.16em, .28vw, .24em);
  text-transform: uppercase;
  line-height: 1.35;
  color: var(--ink);
  white-space: nowrap;
}

/* The wordmark is a home link on the 404 page only. */
.site-name--link { text-decoration: none; }
.site-name--link:hover { color: var(--green); }

.site-place {
  margin-left: auto;
  font-size: 12.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  line-height: 1.35;
  text-align: right;
  color: var(--ink-muted);
}
/* --- Hero ------------------------------------------------------------- */
/* Base values are the NO-PORTRAIT hero (mockup variant A). Deleting the
   .hero-portrait element is the whole mechanism: the :has() block below
   stops matching and the hero rebalances back to variant A on its own. */

.hero {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(30px, 10vw, 104px) var(--pad-x) clamp(30px, 6vw, 66px);
}

.hero-title {
  margin: 0;
  font-size: clamp(36px, 9.4vw, 88px);
  line-height: 1;
  letter-spacing: -.04em;
  font-weight: 700;
  max-width: 15em;
  text-wrap: balance;
}

.hero-rule {
  width: 44px;
  height: 3px;
  background: var(--green);
  margin: clamp(22px, 4vw, 38px) 0 18px;
}

.hero-sub {
  margin: 0;
  font-size: clamp(18px, 4.6vw, 24px);
  line-height: 1.45;
  max-width: 25em;
  text-wrap: balance;
}

.hero-cta {
  margin-top: clamp(28px, 5vw, 44px);
  display: flex;
  flex-wrap: wrap;
  gap: 12px 26px;
  align-items: center;
}

/* The two audience links, one per fee group (one "See pricing" link from
   1000px). One row beside the button where they fit; on a phone they stack tightly under it, which costs less
   height than letting each wrap onto its own spaced row. */
.hero-routes {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 26px;
}
@media (max-width: 599px) {
  .hero-routes { flex-basis: 100%; flex-direction: column; align-items: flex-start; gap: 0; }
}
/* From 1000px both fee groups sit side by side under one heading row, so
   one "See pricing" link replaces the two audience links; below that the
   groups stack and each link goes to its own group. */
.link-arrow.link-arrow--both { display: none; }
@media (min-width: 1000px) {
  .hero-routes .link-arrow { display: none; }
  .hero-routes .link-arrow.link-arrow--both { display: inline-flex; }
}

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
  /* Text alone is 22px tall, short of the 24px minimum in WCAG 2.2 SC 2.5.8.
     The padding costs nothing: this row's height is set by the button beside
     it, and the underline lives on .link-arrow-text, so it does not move. */
  padding-block: 5px;
}
.link-arrow:hover { color: var(--green); }

/* Underline and arrow follow the link colour, so hover turns all three. */
.link-arrow-text {
  border-bottom: 2px solid currentColor;
  padding-bottom: 2px;
}

.link-arrow-mark {
  width: 9px;
  height: 9px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  margin-top: -4px;
}
.link-arrow:hover .link-arrow-mark { transform: translateY(2px) rotate(45deg); }

/* Portrait. Written for the real <img>: height:auto lets aspect-ratio win
   over the width/height attributes (which stay in the markup to reserve space
   and prevent layout shift), and object-fit crops. Hidden below 1000px; see
   the media query under the credential strip. Padding and the flex label
   layout belong to the placeholder box only, never to the image. */
.hero-portrait {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}
.hero-portrait.ph-box {
  display: flex;
  align-items: flex-end;
  padding: 14px;
}

/* Credential strip */
.cred-strip {
  list-style: none;
  margin: clamp(38px, 6.4vw, 60px) 0 0;
  padding: 0;
  display: grid;
  /* One column, then three. Never auto-fit: between ~490 and 740px that
     produced two-plus-one, with a credential alone on its own row. */
  grid-template-columns: 1fr;
  row-gap: 24px;
  column-gap: clamp(24px, 3.4vw, 44px);
}

.cred {
  border-top: 1px solid var(--on-field-rule);
  padding: 17px 0 0;
}

.cred-head {
  margin: 0;
  font-size: clamp(17px, 4.3vw, 20px);
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.2;
}
/* The teacher credential carries two sub lines, set as one block with no gap
   between them. The strip ends 3px above the fold at 1366x657 with them, so
   anything added here needs the fold re-measured. */
.cred-sub + .cred-sub { margin-top: 0; }
/* "Ten years, PBIS coordinator, coached multiple state champions" wraps in
   the three-column strip; balanced, it never leaves "champions" alone. */
.cred-sub { text-wrap: balance; }

.cred-sub {
  margin: 5px 0 0;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--ink-muted);
}

/* The portrait is a wide-viewport element only. Narrower than 1000px it
   renders as a full-bleed band wedged into the text stack, which costs 276px
   and pushed the primary CTA off a 664px-tall screen entirely. Below that the
   page uses the approved no-portrait comp (variant A); the full-width
   photograph further down carries her presence there. Breakpoint rationale is
   on the grid block near the foot of this file. */
@media (max-width: 999px) {
  .hero-portrait { display: none; }
}

@media (min-width: 860px) {
  .cred-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Wide-screen hero without a portrait. Comp A's values (88px headline, 104px
   top padding, a 25em subhead running to four lines) put the whole credential
   strip below the fold on a 664px-tall laptop screen: bottom edge at y=746 at
   1280x664, the failure #7 fixed for the portrait variant. These bring it to
   ~y=650, measured in Firefox with Arial metrics. The headline still holds two
   lines from 1000px up. The portrait variant's own :has() rules below still
   win where they overlap; the top padding applies to both and only helps.
   Below 1000px nothing changes. */
@media (min-width: 1000px) {
  .hero        { padding-top: clamp(30px, 6vw, 80px); }
  .hero-title  { font-size: clamp(36px, 5.6vw, 76px); }
  .cred-strip  { margin-top: clamp(30px, 4.4vw, 44px); }
}

/* The five values that differ once a portrait is present (mockup variant B).
   No JavaScript, no duplicated markup: presence of the element is the
   condition. Scoped to the breakpoint that actually shows the portrait, so
   the hidden-portrait case below 1000px falls through to variant A. Deleting
   the element still reproduces variant A at every width, as before.

   The headline max beside the portrait is 56px (66px before #12's longer
   headline), not the comp's 76px. At 76px the headline needs
   ~900px to hold two lines and the portrait column leaves at most 828px, so
   it wrapped to three. That third line was 78px and put the credential strip
   below the fold on a 664px-tall viewport. Portrait and headline had to come
   down together: shrinking either alone just hands the constraint to the
   other column. See the max-width note on the grid below. */
@media (min-width: 1000px) {
  .hero:has(.hero-portrait) .hero-title {
    /* 4.4vw / 56px since the two-audience headline (#12), so its two lines,
       split at the comma, still fit the text column beside the portrait. */
    font-size: clamp(36px, 4.4vw, 56px);
    line-height: 1.02;
  }
  .hero:has(.hero-portrait) .hero-rule  { margin-top: clamp(22px, 3.4vw, 32px); }
  .hero:has(.hero-portrait) .hero-sub   { font-size: clamp(18px, 4.6vw, 22px); }
  .hero:has(.hero-portrait) .hero-cta   { margin-top: clamp(26px, 3.4vw, 36px); }
  .hero:has(.hero-portrait) .cred-strip { margin-top: clamp(30px, 4.4vw, 44px); }
}

/* 404 page: see "404 page" at the end of the field edition. */

/* --- Fees ------------------------------------------------------------- */

.fees {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(32px, 6vw, 74px) var(--pad-x) 0;
}

/* Two fee groups, one per audience, each the landing point of one hero
   link. Equal weight by construction: the same heading, the same cards, the
   same spacing. The group heading is the only heading; nothing small sits
   above it. scroll-margin keeps the heading clear of the top edge when a
   hero link jumps here. */
.fee-group-title { scroll-margin-top: 16px; }
.fee-group + .fee-group { margin-top: clamp(52px, 8vw, 96px); }

.fee-group-title {
  margin: 0 0 clamp(16px, 2.4vw, 24px);
  font-size: clamp(28px, 7vw, 40px);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.05;
}

/* Stacked cards share one hairline: no gap, and every card after the first
   pulls up 1px so adjacent borders overlap instead of doubling. Three columns
   from 1000px, with a real gap. Explicit rather than auto-fit, which left one
   card alone on its own row between ~490 and 740px, and whose fluid gap and
   margin blurred the shared hairline on 390-430px phones. Not earlier than
   1000px: below that the fee names wrap ("Chapter / session"). */
.fee-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
}

.fee-card + .fee-card { margin-top: -1px; }

@media (min-width: 1000px) {
  .fee-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 18px;
  }
  .fee-card + .fee-card { margin-top: 0; }
}

.fee-card {
  border: 1px solid var(--rule-card);
  padding: clamp(20px, 3vw, 30px);
  display: grid;
  gap: 9px;
  align-content: start;
}

.fee-name {
  margin: 0;
  font-size: clamp(22px, 5.4vw, 29px);
  font-weight: 700;
  letter-spacing: -.03em;
}

.fee-price {
  margin: 0;
  font-size: clamp(21px, 4.9vw, 27px);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1.1;
  /* Prices in the brand green, not grey: a grey price under a black fee
     name read as secondary, and the price is what a buyer scans for. */
  color: var(--green);
}

.fee-unit {
  /* Half the price. The display price never drops below 38px, so "/hr"
     never drops below 19px. */
  font-size: .5em;
  font-weight: 700;
  letter-spacing: .04em;
  /* Same green as the price, so "$100/hr" reads as one figure. In muted grey
     the unit looked like a footnote stuck to the price. */
  color: inherit;
  margin-left: 2px;
}

.fee-desc {
  margin: 2px 0 0;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--ink-card);
  /* No lone last word ("available.", "program.") on two-line cards. */
  text-wrap: pretty;
}

/* The travel note: a caption under each group's cards, worded the same in
   both so each group reads complete on its own. No rules and no indent: it
   qualifies the prices above it and sits on the same left edge as the
   heading and the button. Card description size and ink, so it reads as a
   term of booking, not fine print. Both groups close the same way, caption
   then button, so neither ends heavier than the other. Until 2026-11-10 the
   notes were ruled rows with a season note above; see the copy sheet. */
.fee-note {
  margin: 12px 0 0;
  max-width: 50em;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--ink-card);
}

/* Second call to action, where the decision happens, once per fee group.
   On a phone the contact section is still screens further down from here.
   It is the same solid button as the hero's: the same action carries the
   same weight, and right after the prices is where it is most needed. */
.fee-cta { margin-top: 24px; }

/* --- Full-width photograph -------------------------------------------- */
/* Owns its own top margin, so deleting the element leaves no gap behind.
   Written for the real <img>, as with the portrait: height:auto so
   aspect-ratio beats the height attribute, and no padding, which would inset
   the photograph inside a frame of page background. 24/9 is a 146px sliver
   on a 390px phone, so narrow screens get 4/3. */

.fullbleed {
  display: block;
  width: 100%;
  height: auto;
  margin-top: clamp(36px, 7vw, 90px);
  aspect-ratio: 24 / 9;
  object-fit: cover;
}
.fullbleed.ph-box {
  display: flex;
  align-items: flex-end;
  padding: 16px var(--pad-x);
}

@media (max-width: 699px) {
  .fullbleed {
    aspect-ratio: 4 / 3;
    object-position: 50% 40%;
  }
}

/* --- Labelled bands: About, Speaking, Advisors ------------------------ */
/* Each owns its own top padding, so deleting a whole <section> leaves no
   empty container and no orphaned spacing. */

.band {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: clamp(30px, 6vw, 70px) var(--pad-x) 0;
  display: flex;
  flex-wrap: wrap;
  gap: 14px 60px;
}

/* About follows the full-width photograph and carries the wider opening.
   The 64px floor is for phones and tablets, where the fee groups stack 52px
   apart: at the old 36px the step from fees into About, a new section on
   the same cream, was smaller than the step between the two fee groups, so
   About read as a third group of prices. 7vw passes 64px at 915px, so
   desktop is unchanged. */
.band--about { padding-top: clamp(64px, 7vw, 90px); }

.band-label {
  margin: 0;
  flex: 0 0 170px;
  padding-top: 5px;
  font-size: 12.5px;
  font-weight: 400;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.band-body {
  flex: 1 1 420px;
  min-width: 0;
  display: grid;
  gap: 14px;
}

/* About: one lead sentence carrying the weight, then two body paragraphs. */
.band-body--about {
  gap: 16px;
  max-width: 33em;
}

.about-lead {
  margin: 0;
  font-size: clamp(19px, 4.6vw, 21px);
  line-height: 1.5;
  letter-spacing: -.02em;
  color: var(--ink);
}

.about-para {
  margin: 0;
  font-size: 17px;
  line-height: 1.7;
  color: var(--ink-soft);
}

.video-slot {
  width: 100%;
  max-width: 600px;
  aspect-ratio: 16 / 9;
  border: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* One line under the video saying where and when it was filmed. */
.video-caption {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink-muted);
  max-width: 31em;
}

/* Advisors. Wider column than the bio on purpose: quotes carry more weight
   at the same size, so they get more measure. */
.band-body--quotes {
  gap: clamp(24px, 3vw, 34px);
  max-width: 44em;
}

.quote {
  margin: 0;
  display: grid;
  gap: 12px;
}

.quote blockquote {
  margin: 0;
  font-size: clamp(19px, 4.6vw, 21px);
  line-height: 1.55;
  letter-spacing: -.02em;
  color: var(--ink);
  text-wrap: pretty;
}

.quote figcaption {
  font-size: 12.5px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Second and any subsequent quote sits under a rule, with the column gap
   mirrored above it so the divider reads as shared between the two. */
.quote--divided {
  border-top: 1px solid var(--rule-quote);
  padding-top: clamp(24px, 3vw, 34px);
}

/* --- Contact ---------------------------------------------------------- */

.contact {
  margin-top: clamp(42px, 8vw, 110px);
  background: var(--bg-panel);
  padding: clamp(32px, 7vw, 84px) var(--pad-x);
}

.contact-inner {
  /* .contact carries the side padding itself, outside this box, so the
     padding comes off the max-width here. Plain --maxw put the contact
     column up to 80px left of every other section on wide screens. */
  max-width: calc(var(--maxw) - 2 * var(--pad-x));
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 32px 60px;
}

.contact-pitch {
  flex: 1 1 320px;
  min-width: 0;
}

.contact-title {
  margin: 0;
  font-size: clamp(26px, 6.6vw, 46px);
  line-height: 1.06;
  letter-spacing: -.035em;
  font-weight: 700;
  max-width: 15em;
  /* Keeps "for." from sitting alone on the last line around 768px. */
  text-wrap: balance;
}

.contact-sub {
  margin: 18px 0 0;
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 25em;
}

/* School-admin small print: purchase orders and district paperwork. Sits
   with the quote promise, where the person who has to process the booking
   is reading, rather than among the prices. */
.contact-note {
  margin: 12px 0 0;
  font-size: 15px;
  line-height: 1.6;
  color: var(--ink-muted);
  max-width: 28em;
}

.contact-email {
  margin: 24px 0 0;
  font-size: clamp(17px, 4.4vw, 25px);
  font-weight: 700;
  letter-spacing: -.02em;
}

.contact-email a {
  text-decoration: none;
  border-bottom: 2px solid var(--green);
  word-break: break-word;
}

.contact-form-wrap {
  flex: 1 1 320px;
  min-width: 0;
}

.contact-form {
  display: grid;
  gap: 14px;
  align-content: start;
}

/* The label IS the field box. Underlined inputs alone were ambiguous: on an
   empty form you could not tell whether a caption belonged to the rule above
   it or the one below. Grouping is structural now, not spacing-based, so the
   caption and its input visibly share one container. Do not put a border back
   on the input; the box belongs to the label. */
.field {
  display: grid;
  gap: 3px;
  background: var(--bg);
  border-bottom: 1px solid var(--rule-input);
  padding: 11px 13px 4px;
}

/* Ink Soft (9.68:1). Until #21 these were Ink Muted (4.97:1), the lowest
   contrast on the page. Sentence case at 14.5px was tried and rejected
   (Jeff, 2026-11-11): the tracked capitals suit the page better. In
   capitals "#1 thing audience should walk away with" wraps to two lines
   on a phone; only shorter wording would fix that. */
.field-label {
  font-size: 12.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.field-input {
  width: 100%;
  font: 16px var(--font);
  padding: 3px 0 8px;
  border: 0;
  background: transparent;
  color: var(--ink);
  letter-spacing: 0;
  text-transform: none;
}

textarea.field-input { resize: vertical; }

/* Focus lands on the box, not the input: a 2px ring 3px outside it, like
   the buttons' focus. Outside, because the ring then sits on the ground
   around the box, where it can be seen; until #26 it was a bar inside the
   cream box, 1.56:1 in lilac. An outline, not a shadow, so it survives
   forced-colours modes and nothing shifts. The step-2 chip groups are left
   out: each chip draws its own ring. The caption darkening is a second
   signal, so focus does not rest on the ring alone. */
.field:not(.field--choices):focus-within {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

.field:focus-within .field-label { color: var(--ink); }

/* Step 2. Pick-one answers are tap-to-choose chips, one native radio under
   each, so the arrow keys, the checked state and the screen-reader name all
   come from the browser. The fieldset keeps the field box's look. */
.field--choices {
  display: block;
  min-width: 0;
  margin: 0;
  border: 0;
  border-bottom: 1px solid var(--rule-input);
  padding: 11px 13px 13px;
}
.field--choices legend {
  float: left;
  width: 100%;
  padding: 0;
  margin-bottom: 9px;
}
.choices {
  clear: both;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.choice {
  position: relative;
  display: flex;
}
.choice input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.choice span {
  display: flex;
  align-items: center;
  min-height: 42px;
  padding: 8px 13px;
  border: 1px solid var(--rule-input);
  font-size: 15px;
  line-height: 1.2;
  color: var(--ink);
}
.choice:hover span { border-color: var(--ink-muted); }
.choice input:checked + span {
  background: var(--green);
  border-color: var(--green);
  color: #fff;
}
.choice input:focus-visible + span {
  outline: 2px solid var(--green);
  outline-offset: 2px;
}

/* A rule and the display face mark where the second form starts; at 20px
   in the body face the heading disappeared between two light boxes. */
.details-head {
  margin: 12px 0 2px;
  padding-top: 26px;
  border-top: 1px solid var(--on-field-rule);
}
.details-title {
  margin: 0;
  font-family: var(--display);
  font-size: 32px;
  font-weight: 700;
  line-height: .95;
  letter-spacing: .01em;
  text-transform: uppercase;
}
.details-sub {
  margin: 8px 0 0;
  font-size: 16px;
  line-height: 1.5;
}

.contact-form .btn {
  justify-self: start;
  margin-top: 4px;
}

/* Honeypot. Off-screen for sighted users, hidden from assistive tech, and
   skipped by the tab order. Anything that fills it is a bot. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
  white-space: nowrap;
}

/* One set of markup and copy serves both paths: :target for the no-script
   redirect, .is-shown for the fetch handler. */
.form-status {
  display: none;
  margin: 0 0 20px;
  padding: 14px 16px;
  border-left: 3px solid;
  font-size: 16px;
  line-height: 1.6;
}
.form-status p { margin: 0; }

.form-status:target,
.form-status.is-shown { display: block; }

/* Once the fetch path has shown a banner it owns both of them. Clearing the
   hash is not enough on its own: history.replaceState tidies the URL but
   leaves the document's :target element untouched, so /#form-error keeps
   matching with an empty hash and the error banner would render stacked
   under a fresh success. Higher specificity, so it wins wherever it sits. */
.js-status .form-status:target:not(.is-shown) { display: none; }

/* The two steps. Step 1 shows until it has sent, then step 2 takes its
   place under the "Got it" banner; once step 2 has sent, neither shows and
   the page reads as done rather than ready to send again.

   form.js drives this with data-step on the wrap. With scripting off the
   Function redirects to /#form-sent, then /#details-sent, and :target does
   the same job. The :target half only applies before form.js has taken
   over the banners, for the same stale-target reason as the rule above. */
.details-form,
.field[hidden] { display: none; }

.contact-form-wrap[data-step="details"] #details-form,
html:not(.js-status) #form-sent:target ~ #details-form,
html:not(.js-status) #details-error:target ~ #details-form { display: grid; }

.contact-form-wrap[data-step] #contact-form,
.contact-form-wrap[data-step="done"] #details-form,
html:not(.js-status) #form-sent:target ~ #contact-form,
html:not(.js-status) #details-sent:target ~ #contact-form,
html:not(.js-status) #details-error:target ~ #contact-form { display: none; }

/* A failed step 2 lands on /#details-error, but the inquiry did go, so
   "Got it." shows above it just as form.js shows the pair. Only one
   element can be the :target, hence :has. */
html:not(.js-status) .contact-form-wrap:has(#details-error:target) #form-sent { display: block; }

/* --- Footer ----------------------------------------------------------- */

.site-footer {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 28px var(--pad-x) 46px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 24px;
  justify-content: space-between;
  /* The link's tap-target padding makes it taller than the site name;
     baseline keeps the two lines of text level. */
  align-items: baseline;
  /* The size of the fee notes, the page's other sentence-case small print.
     12.5px matched the tracked uppercase labels, and without tracking it
     read smaller than the rest of the page. */
  font-size: 14px;
  color: var(--ink-muted);
}

/* 14px text gives about a 16px tap target, under the 24px minimum in WCAG 2.2
   SC 2.5.8. This link underlines with text-decoration, which is drawn from the
   baseline, so padding grows the target without moving anything visible. The
   contact section's address is deliberately left alone: it underlines with a
   border, which padding would push away from the text. */
.site-footer a {
  display: inline-block;
  padding-block: 6px;
}

/* ==========================================================================
   Portrait beside the text.

   The mockup switched at innerWidth <= 719 (a DCLogic resize listener). This
   build switches at 1000px instead. Below that the portrait's own minimum
   (240px) leaves the headline a ~394px column, which drove the headline down
   to 39px over three lines: a two-column hero that is worse than no portrait
   at all. At 1000px the text column reaches ~606px and the headline holds two
   lines at 51px. Under 1000px the page uses the approved no-portrait comp
   (variant A) and the headline runs full width.
   ========================================================================== */

@media (min-width: 1000px) {

  /* Explicit rows and areas, not auto-placement: with no explicit rows,
     `grid-row: 1 / -1` resolves to a single-row span and the portrait would
     silently stop spanning the column. */
  .hero-main:has(.hero-portrait) {
    display: grid;
    /* Narrowed from the comp's clamp(240px, 26vw, 360px). Paired with the
       headline max above: the wider column is what forced the third
       headline line. */
    grid-template-columns: minmax(0, 1fr) clamp(240px, 22vw, 300px);
    grid-template-rows: auto auto auto auto;
    column-gap: clamp(26px, 3.4vw, 56px);
    align-items: start;
  }

  .hero-main:has(.hero-portrait) > .hero-title { grid-area: 1 / 1; }
  .hero-main:has(.hero-portrait) > .hero-rule  { grid-area: 2 / 1; }
  .hero-main:has(.hero-portrait) > .hero-sub   { grid-area: 3 / 1; }
  .hero-main:has(.hero-portrait) > .hero-cta   { grid-area: 4 / 1; }

  .hero-main:has(.hero-portrait) > .hero-portrait {
    grid-area: 1 / 2 / 5 / 3;
    align-self: start;
    width: 100%;
  }
}

/* ==========================================================================
   Field edition (bolder variant).

   Same copy, structure and behaviour; a louder voice. Two additions to the
   system and nothing else:
   - Barlow Condensed, self-hosted (CSP font-src 'self'), for every display
     line. Drawn from California highway signs and plates, the roads every
     chapter visit runs on. Body text stays Helvetica.
   - One light accent, used only on the dark fields: the hero rule, the
     buttons there, focus and hover. Never as text on the light ground.
   The brand colour becomes a field that owns the page's two ends: header and
   hero at the top, contact and footer at the close.
   ========================================================================== */

@font-face {
  font-family: 'Barlow Condensed';
  src: url('/fonts/barlow-condensed-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Barlow Condensed';
  src: url('/fonts/barlow-condensed-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Stand-ins for Barlow while it downloads (#28). Without them the display
   lines fall back to Helvetica or Arial, about 1.44 times as wide, and the
   headline runs off a 390px phone. Each stand-in is a bold system font
   shrunk to Barlow's width, so lines break in the same places and the fold
   holds before the swap. The first three are condensed faces, which need
   little shrinking and so keep Barlow's weight; the last two are wide faces
   shrunk hard, which read lighter, for devices with none of the first three.

   size-adjust: Barlow's width over the stand-in's, letters only (it
     doesn't scale letter-spacing), measured across every display line on
     the page. Set at or under the tightest line, "Elk Grove, California"
     in the header, which fits on one line at 360px with only a pixel or two
     to spare even in Barlow; any wider and the header wraps and the page
     drops 15px.
   ascent/descent: Barlow's 100% and 20%, divided by size-adjust, so the
     text sits on Barlow's baseline.
   Each face covers 600 with its bold: the difference doesn't show in the
   moment before the swap.
   Safari before 17 (iOS 16 and older) ignores size-adjust and shows the
   stand-in full size: wider than Barlow, but still narrower than the
   Helvetica these devices showed before, so nothing gets worse.
   Checked on a real iPhone (iOS 18, Safari) and Mac (Chrome 147, Safari
   15.6), 2026-11-12: right stand-in picked, no sideways scroll, same line
   breaks as Barlow.

   1. Arial Narrow Bold: Macs, Windows with Office, Linux (Liberation Sans
      Narrow shares its metrics). Header 0.80, headline 0.81-0.87.
   2. Helvetica Neue Condensed Bold: iPhones, which have no Arial Narrow.
      Measured on an iPhone (2026-11-12): header 0.863, headline 0.87-0.91.
   3. Roboto Condensed Bold: most Android phones. Header 0.82.
   4. Arial or Helvetica Bold, shrunk: anything else (Windows without
      Office). Header 0.66.
   5. Roboto Bold, shrunk: Android without Roboto Condensed. Header 0.73. */
@font-face {
  font-family: 'Barlow Fallback Narrow';
  src: local('Arial Narrow Bold'), local('ArialNarrow-Bold'),
       local('Liberation Sans Narrow Bold'), local('LiberationSansNarrow-Bold');
  font-weight: 600 700;
  size-adjust: 80%;
  ascent-override: 125%;
  descent-override: 25%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Barlow Fallback Condensed';
  src: local('Helvetica Neue Condensed Bold'), local('HelveticaNeue-CondensedBold');
  font-weight: 600 700;
  size-adjust: 85%;
  ascent-override: 118%;
  descent-override: 24%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Barlow Fallback Roboto Condensed';
  src: local('Roboto Condensed Bold'), local('RobotoCondensed-Bold');
  font-weight: 600 700;
  size-adjust: 82%;
  ascent-override: 122%;
  descent-override: 24%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Barlow Fallback Arial';
  src: local('Arial Bold'), local('Arial-BoldMT'),
       local('Helvetica Bold'), local('Helvetica-Bold'),
       local('Liberation Sans Bold'), local('LiberationSans-Bold');
  font-weight: 600 700;
  size-adjust: 65%;
  ascent-override: 154%;
  descent-override: 31%;
  line-gap-override: 0%;
}
@font-face {
  font-family: 'Barlow Fallback Roboto';
  src: local('Roboto Bold'), local('Roboto-Bold');
  font-weight: 600 700;
  size-adjust: 72%;
  ascent-override: 139%;
  descent-override: 28%;
  line-gap-override: 0%;
}

:root {
  --display:      'Barlow Condensed', 'Barlow Fallback Narrow', 'Barlow Fallback Condensed',
                  'Barlow Fallback Roboto Condensed', 'Barlow Fallback Arial', 'Barlow Fallback Roboto',
                  var(--font);

  /* The field is the brand green. Ratios on it: */
  --field:        #163a2b;
  --on-field:     #f3f1ea;   /* text                               11.10:1  */
  --on-field-sub: #b5beb5;   /* secondary text, footer              6.57:1  */
  --on-field-rule:#476255;   /* hairlines, decorative                       */

  /* The accent, lilac. Only on the field, never as text on the light
     ground: it is 6.99:1 on the field and 1.6:1 on the cream. */
  --accent:        #cdb8f0;
  --accent-hover:  #dacaf4;
  --accent-ink:    #0a1a13;   /* labels on the accent              10.01:1  */
  --accent-busy:   #9692b5;   /* send in flight; label on it        6.05:1  */
  --accent-edge:   #7d5abc;   /* floating button's edge, deeper violet;
                                 its boundary on the cream          4.74:1  */
}

/* Browser surfaces in the palette. Selected text takes the brand green on
   the light ground and the accent on the field (the form's light boxes
   included), the same pairs the buttons use. The caret is the green. */
::selection { background: var(--green); color: #fff; }
.panel ::selection,
.contact ::selection,
.site-footer ::selection { background: var(--accent); color: var(--accent-ink); }
.field-input { caret-color: var(--green); }

/* --- The field: header, hero, contact, footer ------------------------- */

.panel,
.contact { background: var(--field); }

.panel a:focus-visible,
.panel button:focus-visible,
.contact a:focus-visible,
.contact button:focus-visible,
.site-footer a:focus-visible { outline-color: var(--accent); }

/* The header rule runs the full width, like the footer's. On the header
   itself it stopped at the 1320px column's padding edge, past the content
   on either side, on screens wider than ~1340px. */
.panel:has(> .site-header) { border-bottom: 1px solid var(--on-field-rule); }
.site-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(17px, 1.5vw, 21px);
  letter-spacing: .14em;
  color: var(--on-field);
}
.site-place {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(14px, 1.2vw, 16px);
  letter-spacing: .14em;
  color: var(--on-field-sub);
}

.hero { color: var(--on-field); }

.hero-title {
  font-family: var(--display);
  font-weight: 700;
  text-transform: uppercase;
  /* One phrase per line below 1000px (see "Headline lines" below), so the
     size only has to fit the longest phrase, "or the presentation.": 10vw
     on phones, capped at 56px on tablets. */
  font-size: clamp(36px, 10vw, 56px);
  line-height: .92;
  letter-spacing: -.005em;
  max-width: none;
}

/* Headline lines. The headline is a list of three moments, and it breaks
   only between its phrases, never after "the": below 1000px one phrase per
   line (READY BEFORE / THE INTERVIEW, / THE CONTEST, / OR THE PRESENTATION.),
   from 1000px two lines split at the comma. Letting the browser wrap it put
   "the" at the end of a line at every width, and at 88px the comma split did
   not fit at all, which is why the headline came down to 76px. */
.ht-p { white-space: nowrap; }
@media (max-width: 999px) {
  .ht-p { display: block; }
}
@media (min-width: 1000px) {
  .ht-line { display: block; }
}

/* Tablets. The subhead's 24px desktop size jumped in straight from the
   phones' 17px at 600px; 20px bridges it. 30em sets it as four even lines
   at 768px instead of five at the base 25em. */
@media (min-width: 600px) and (max-width: 999px) {
  .hero-sub { font-size: 20px; max-width: 30em; }
}

.hero-rule {
  width: 72px;
  height: 6px;
  background: var(--accent);
}

.hero-sub { color: var(--on-field-sub); }

.hero .btn {
  background: var(--accent);
  color: var(--accent-ink);
}
.hero .btn:hover,
.hero .btn:active { background: var(--accent-hover); color: var(--accent-ink); }

.btn {
  font-family: var(--display);
  font-size: 19px;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 16px 30px 15px;
  /* Fixed, not 'normal': 'normal' comes from the font, so a stand-in made
     this line a few px shorter on an iPhone and the page jumped when Barlow
     arrived (#28). Barlow's own 'normal' here is 23.3px. */
  line-height: 23px;
}

.hero .link-arrow { color: var(--on-field); }
/* The base hover colours are the brand colour, which is the field itself:
   on the field a hovered link would vanish. */
.site-name--link:hover { color: var(--accent); }
.skip-link:hover { color: #fff; }
.hero .link-arrow:hover { color: var(--accent); }
.link-arrow {
  font-family: var(--display);
  font-size: 19px;
  letter-spacing: .08em;
  text-transform: uppercase;
  /* Fixed, not 'normal': 'normal' comes from the font, so a stand-in made
     this line a few px shorter on an iPhone and the page jumped when Barlow
     arrived (#28). Barlow's own 'normal' here is 23.3px. */
  line-height: 23px;
}

.cred-head {
  font-family: var(--display);
  font-weight: 700;
  text-transform: uppercase;
  font-size: clamp(21px, 5.4vw, 26px);
  letter-spacing: .01em;
  line-height: 1.05;
  color: var(--on-field);
}
.cred-sub { color: var(--on-field-sub); }

/* The display face is taller per line than Helvetica was, so the hero gives
   back some air to keep the credential strip above the fold at 1280x664 and
   390x844, the same targets as the restrained version. The header's 15px
   block padding is part of that budget (see .site-header). */
.hero-rule  { margin: clamp(20px, 3vw, 30px) 0 16px; }
.cred-strip  { row-gap: 16px; }
.cred        { padding-top: 14px; }

@media (min-width: 1000px) {
  .hero        { padding-top: clamp(30px, 4.2vw, 60px); }
  /* 56px at 1000px, continuous with the tablet cap, up to 76px. At 88px
     the second line, "the contest, or the presentation.", no longer fit
     the column and the strip fell below the fold. */
  .hero-title  { font-size: clamp(46px, 5.6vw, 76px); }
  .hero-cta    { margin-top: 34px; }
  .cred-strip  { margin-top: 36px; }
  /* One balanced run, both audiences in one sentence: three lines of 70
     to 75 characters, about 104px tall, no taller than the headline. 36em
     rather than the 35em it needs in Firefox leaves slack for Mac
     Helvetica. At 213 characters; a fourth line comes back past about
     225, so measure before lengthening it. */
  .hero-sub    { max-width: 36em; }
}

/* Phones. The two-audience hero is taller than the one it replaced: a
   longer subhead and two audience links instead of one. These spacing
   values pay for that so the credential strip still ends inside a 390x844
   screen. Type stays at or above 17px; only the air between parts shrinks. */
@media (max-width: 599px) {
  .hero        { padding-top: 28px; }
  .hero-rule   { margin: 18px 0 14px; }
  .hero-sub    { font-size: 17px; line-height: 1.42; }
  .hero-cta    { margin-top: 24px; row-gap: 10px; }
  .hero-routes .link-arrow { padding-block: 3px; }
  .cred-strip  { margin-top: 28px; row-gap: 10px; }
  .cred        { padding-top: 11px; }
}

/* --- Fees, on the light ground ----------------------------------------- */

.band-label {
  font-family: var(--display);
  font-weight: 600;
  font-size: 16px;
  letter-spacing: .14em;
  color: var(--ink);
  /* Fixed, not 'normal': 'normal' comes from the font, so a stand-in made
     this line a few px shorter on an iPhone and the page jumped when Barlow
     arrived (#28). Barlow's own 'normal' here is 19.3px. */
  line-height: 19px;
}
.fee-group-title {
  font-family: var(--display);
  text-transform: uppercase;
  font-size: clamp(34px, 8.4vw, 52px);
  letter-spacing: .005em;
  line-height: .95;
}

.fee-card { background: var(--bg-panel); border-color: var(--rule-card); }

.fee-name {
  font-family: var(--display);
  font-weight: 700;
  text-transform: uppercase;
  font-size: clamp(25px, 6vw, 31px);
  letter-spacing: .01em;
  line-height: 1;
}

.fee-price {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(38px, 9vw, 50px);
  letter-spacing: 0;
  line-height: 1;
  margin-top: 4px;
}

/* Floating call to action, phones and tablets only (float-cta.js). One
   "Get a quote" at the bottom left. It appears once the reader is into the
   fees and the hero's own button is gone, and leaves just before the green
   contact section, where "Send inquiry" takes over. While it runs the fee
   groups' own buttons step aside, so there is only ever one.

   In the accent, like the hero's "Ask about a date": on a phone that is the
   action colour, and it only floats over light sections, where dark green
   is the prices' and headings' colour and a green button read as more page.
   A 2px edge in a deeper shade of the accent keeps the light fill from
   melting into the cream. It sits 16px in from the screen's left and bottom,
   off the content's left edge, so it reads as a layer above the page; with
   the shadow that is enough, and it stays square like every other button.
   Off screen it is only faded and made untappable, not removed: it sits in
   the markup straight after the fees, so keyboard and screen reader users
   still reach it there, and it shows itself when it takes keyboard focus. */
.float-cta,
.float-cta[hidden] { display: none; }

@media (max-width: 999px) {
  .float-cta {
    display: inline-block;
    position: fixed;
    left: calc(16px + env(safe-area-inset-left, 0px));
    bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    z-index: 15;
    background: var(--accent);
    color: var(--accent-ink);
    box-shadow: inset 0 0 0 2px var(--accent-edge), 0 14px 32px rgba(0, 0, 0, .3), 0 4px 10px rgba(0, 0, 0, .16);
    opacity: 0;
    pointer-events: none;
    transform: translateY(12px);
  }
  .float-cta.is-shown,
  .float-cta:focus-visible { opacity: 1; pointer-events: auto; transform: none; }
  /* It can take focus over the dark hero as well as the light page, so the
     ring is two-tone: a cream band (fills the outline's offset) inside the
     usual green outline. One of the two always shows. */
  .float-cta:focus-visible {
    outline-offset: 2px;
    box-shadow: inset 0 0 0 2px var(--accent-edge), 0 0 0 2px var(--bg), 0 14px 32px rgba(0, 0, 0, .3), 0 4px 10px rgba(0, 0, 0, .16);
  }
  /* Hover only where there is a pointer that hovers: on touch, :hover sticks
     after a tap and would leave the button lighter on the way out. */
  .float-cta:active { background: var(--accent-hover); color: var(--accent-ink); }

  .has-float-cta .fee-cta { display: none; }
}
@media (max-width: 999px) and (hover: hover) {
  .float-cta:hover { background: var(--accent-hover); color: var(--accent-ink); }
}

@media (max-width: 999px) and (prefers-reduced-motion: no-preference) {
  .float-cta {
    transition: opacity .22s cubic-bezier(.22, 1, .36, 1), transform .22s cubic-bezier(.22, 1, .36, 1), background-color .15s;
  }
}

/* Fees side by side, from 1000px. Two equal columns, schools on the left,
   each stacking its three cards on a shared hairline, with one travel note
   and one button under both. Two identical closes on one laptop screen read
   as copy and paste; on phones the groups stay stacked, each with its own
   close, because there they are never on screen together and the per-group
   button keeps a school reader one short scroll from acting (the reason a
   single close was rejected before; see the copy sheet).

   The groups and their card grids become display: contents so headings,
   cards and closes share one grid: headings in row 1 (bottom-aligned), the
   three cards in rows 2 to 4, so each school card is exactly as tall as the
   organization card beside it, and the shared close in row 5 across both
   columns. The hero links target the
   headings, since a display: contents box has nothing to scroll to.

   Slightly tighter than the stacked cards: headings at 44px hold "For
   schools and chapters" to one line in a half-width column, and smaller
   card padding, names and prices keep each column compact. */
@media (min-width: 1000px) {
  .fees {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(28px, 3.2vw, 44px);
    align-items: start;
  }
  .fee-group { display: contents; }
  .fee-group--schools > * { grid-column: 1; }
  .fee-group--orgs > *    { grid-column: 2; }
  .fee-group-title {
    grid-row: 1;
    align-self: end;
    font-size: clamp(36px, 3.6vw, 44px);
    margin-bottom: 18px;
  }
  .fee-grid { display: contents; }
  .fee-card:nth-child(1) { grid-row: 2; }
  .fee-card:nth-child(2) { grid-row: 3; }
  .fee-card:nth-child(3) { grid-row: 4; }
  .fee-card + .fee-card { margin-top: -1px; }
  .fee-card { padding: 22px 26px 24px; gap: 7px; align-self: stretch; }
  .fee-name  { font-size: 27px; }
  .fee-price { font-size: 44px; }
  .fee-group--schools .fee-close { display: none; }
  .fee-group--orgs .fee-close { grid-row: 5; grid-column: 1 / -1; }
  /* The shared note serves both columns. At 12px it sat tight under the
     school column and read as that group's footnote. A one-row close with
     the button at the right was tried for #21 and rejected (Jeff,
     2026-11-11): the button reads better on the left edge. */
  .fee-close .fee-note { margin-top: 22px; }
}

/* --- About ------------------------------------------------------------- */

.about-lead {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(26px, 6.4vw, 32px);
  line-height: 1.12;
  letter-spacing: 0;
  /* Even lines, so "room." never sits alone on the last one. */
  text-wrap: balance;
}

/* "She coaches the whole room": the page's one positioning idea, lifted
   out of the second paragraph as the band's closing statement so a
   skimming reader meets it (#21). Same words as before, closing sentence
   included. Flush left in the display face and the brand green, under a
   short green rule like the hero's, so it reads as her statement rather
   than a quotation (an indent and a side bar read as a pull quote).
   Chosen by Colleen on 2026-11-11 over the same rule on the first
   sentence alone, with the closing sentence as body text under it. */
.about-room {
  margin: 10px 0 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(22px, 5.4vw, 26px);
  line-height: 1.15;
  color: var(--green);
  text-wrap: balance;
}
.about-room::before {
  content: "";
  display: block;
  width: 44px;
  height: 3px;
  margin-bottom: 16px;
  background: var(--green);
}

/* --- Contact and footer: the closing field ----------------------------- */

.contact { color: var(--on-field); }
.contact-title {
  font-family: var(--display);
  font-weight: 700;
  text-transform: uppercase;
  font-size: clamp(36px, 9vw, 64px);
  line-height: .95;
  letter-spacing: 0;
  max-width: 11em;
}
.contact-sub  { color: var(--on-field); }
.contact-note { color: var(--on-field-sub); }
.details-title { color: var(--on-field); }
.details-sub   { color: var(--on-field-sub); }
.contact-email a { color: var(--on-field); border-bottom-color: var(--accent); }
.contact-email a:hover { color: var(--accent); }

.contact .btn { background: var(--accent); color: var(--accent-ink); }
.contact .btn:hover,
.contact .btn:active { background: var(--accent-hover); color: var(--accent-ink); }
.contact .btn:disabled,
.contact .btn:disabled:hover { background: var(--accent-busy); color: var(--accent-ink); }

/* Fields stay light boxes on the green, so typing is on paper, not on paint. */
.field { background: var(--bg); border-bottom-color: var(--rule-input); }
.field:not(.field--choices):focus-within { outline-color: var(--accent); }

/* Field errors. A field that is empty or malformed takes a light red bar
   along its bottom, and a hint appears under it on the field, in
   the same light red and with the same left bar as "That didn't send".
   Not colour alone: the hint is words. Two triggers: :user-invalid, which
   the browser sets once the visitor has touched the field or tried to
   send (this works with scripting off), and aria-invalid, which form.js
   sets when it stops a send, since it turns off the browser's own bubble.
   "That didn't send" stays for failed sends only. While a wrong field has
   focus its ring turns the same light red (6.88:1 on the field), so lilac
   never wraps the red bar, and the ring says where you are and that this
   field needs fixing. The hint sits 12px under the box, clear of the
   ring, and 18px above the next box, so it still reads as its field's. */
.field-hint {
  display: none;
  margin: -2px 0 4px;
  padding: 1px 0 1px 13px;
  border-left: 3px solid var(--err-on-field);
  font-size: 15px;
  line-height: 1.45;
  color: var(--err-on-field);
}
.field:has(> .field-input:user-invalid) + .field-hint,
.field:has(> .field-input[aria-invalid="true"]) + .field-hint { display: block; }
.field:has(> .field-input:user-invalid),
.field:has(> .field-input[aria-invalid="true"]) { box-shadow: inset 0 -3px 0 var(--err-on-field); }
.field:has(> .field-input:user-invalid):focus-within,
.field:has(> .field-input[aria-invalid="true"]):focus-within { outline-color: var(--err-on-field); }

/* The messages sit on the field itself, not in a pale box: a light box
   read as one more form field. A bar marks them, and the lead words ("Got
   it.") take the bar's colour: the accent for sent, a light red for
   failed. Both clear 4.5:1 on the field (6.99:1 and 6.88:1). */
.form-status {
  background: transparent;
  color: var(--on-field);
  border-left: 3px solid var(--accent);
  padding: 2px 0 2px 16px;
  font-size: 17px;
}
/* 2px, like the contact email above: at 1px the pale lavender accent read
   as a plain white underline. */
.form-status a {
  color: var(--on-field);
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.form-status a:hover { color: var(--accent); }
.form-status-lead { color: var(--accent); }
.form-status--err { border-left-color: var(--err-on-field); }
.form-status--err .form-status-lead { color: var(--err-on-field); }
.form-status--err a { text-decoration-color: var(--err-on-field); }

/* Once a step has sent, the pitch stops asking. "Tell me the month..." and
   "Fill out the form..." would tell the reader to do what they just did,
   so both leave the screen; the heading stays for screen readers, as the
   section's name. The two "sent" leads take over as the visible headline,
   in the display capitals and the accent, one step below the heading it
   replaces: the high point of the page, not one more line of text. The
   purchase-order note and the email stay on the left. Failed sends keep
   the pitch: the reader still has to act. A failed step 2 does not: the
   inquiry went, so "Got it." still leads. Same two paths as the steps:
   data-step for form.js, :target before form.js has taken over (see "The
   two steps"). */
.contact-inner:has(.contact-form-wrap[data-step]) .contact-sub,
html:not(.js-status) .contact-inner:has(#form-sent:target, #details-sent:target, #details-error:target) .contact-sub { display: none; }
.contact-inner:has(.contact-form-wrap[data-step]) .contact-title,
html:not(.js-status) .contact-inner:has(#form-sent:target, #details-sent:target, #details-error:target) .contact-title {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.contact-inner:has(.contact-form-wrap[data-step]) .contact-note,
html:not(.js-status) .contact-inner:has(#form-sent:target, #details-sent:target, #details-error:target) .contact-note { margin-top: 0; }
/* Stacked, the headline leads; the small print and email follow it. The
   columns stack below about 796px plus the scrollbar; 795px keeps this to
   widths where they are stacked on every browser, so it never swaps them. */
@media (max-width: 795px) {
  .contact-inner:has(.contact-form-wrap[data-step]) .contact-form-wrap,
  html:not(.js-status) .contact-inner:has(#form-sent:target, #details-sent:target, #details-error:target) .contact-form-wrap { order: -1; }
}
/* 48px on desktop, 34px on phones (Jeff, 2026-11-11): at the contact
   heading's 64px "Thanks, that helps." filled the column and outranked
   everything around it. Still a step above "Help me prepare" (32px). */
.form-status--ok .form-status-lead {
  display: block;
  margin-bottom: 10px;
  font-family: var(--display);
  font-size: clamp(34px, 6vw, 48px);
  font-weight: 700;
  line-height: .95;
  text-transform: uppercase;
}

.site-footer {
  max-width: none;
  background: var(--field);
  color: var(--on-field-sub);
  border-top: 1px solid var(--on-field-rule);
  padding-inline: max(var(--pad-x), calc((100% - var(--maxw)) / 2 + var(--pad-x)));
}
.site-footer a { color: var(--on-field); }
.site-footer a:hover { color: var(--accent); }

/* --- 404 page ----------------------------------------------------------
   Green header and footer, cream between: the home page's bookends without
   its long middle. All green read as too much on a page this short
   (Jeff, 2026-11-11). The body is a column so the footer sits at the
   bottom of a tall screen; each part is stretched to full width, since
   the footer's auto side margins would otherwise shrink it to its text.

   The hero is on the cream, so it takes the light-ground colours. Its one
   accent is the button: lilac with the deeper-violet edge, the same
   exception the floating "Get a quote" makes, since lilac alone is
   1.6:1 on cream and the edge (4.74:1) gives it a boundary. The rule
   under the headline is green; a lilac rule would all but vanish here. */
@media screen {
  body:has(.hero--notfound) {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
  }
  body:has(.hero--notfound) > * { width: 100%; }
  body:has(.hero--notfound) > main { flex: 1; }
}
.hero--notfound { color: var(--ink); }
.hero--notfound .hero-title { color: var(--ink); }
.hero--notfound .hero-rule  { background: var(--green); }
.hero--notfound .hero-sub   { color: var(--ink-soft); }
.hero--notfound .btn {
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: inset 0 0 0 2px var(--accent-edge);
}
.hero--notfound .btn:hover,
.hero--notfound .btn:active { background: var(--accent-hover); color: var(--accent-ink); }
.hero--notfound .link-arrow { color: var(--ink); }
.hero--notfound .link-arrow:hover { color: var(--green); }

/* ==========================================================================
   Print: one clean, forwardable page with the contact path at the bottom.
   ========================================================================== */

@media print {

  @page { margin: 12mm; }

  :root { --pad-x: 0; }

  html,
  body {
    background: #fff;
    color: #000;
    min-height: 0;
  }

  /* Screen-only chrome: navigation, the form, and every placeholder. */
  .skip-link,
  .hero-cta,
  .fee-cta,
  .float-cta,
  .video-slot,
  .hero-portrait,
  .fullbleed,
  .contact-form-wrap,
  .ph-box,
  .specimen { display: none !important; }

  /* US Letter inside a 12mm margin is ~725px, which no longer matches the
     1000px portrait block, so the hero is already single-column in print and
     this rule is belt and braces. Kept because a wider paper size or a
     narrower margin would cross 1000px and bring the grid back, and then the
     second track, a fixed clamp() length, would print as a blank right-hand
     gutter. Same specificity as the rule it undoes, and later in the file, so
     it wins without !important. */
  .hero-main:has(.hero-portrait) { display: block; }

  /* Hide a band whose body holds nothing but specimen copy, so no orphaned
     "About", "Speaking" or "Advisors" label prints above empty space. Drop
     the class as real copy goes in and the band starts printing itself. */
  .band:not(:has(.band-body > :not(.specimen))) { display: none; }

  .panel,
  .contact {
    background: transparent;
    border: 0;
  }

  .site-header { padding: 0 0 10pt; }
  .site-name   { font-size: 9pt; color: #000; }
  .site-place  { font-size: 8pt; color: #333; }

  .hero { padding: 10pt 0 0; }

  /* Paper is under 1000px wide, so undo the one-phrase-per-line stack:
     two lines split at the comma, as on a desktop screen. */
  .ht-p    { display: inline; }
  .ht-line { display: block; }

  .hero-title {
    font-size: 24pt !important;
    line-height: 1.05 !important;
    max-width: 24em;
  }
  .hero-rule { margin: 8pt 0 6pt !important; height: 2px; }
  .hero-sub {
    font-size: 11pt !important;
    max-width: none;
    color: #000;
  }

  .cred-strip {
    margin-top: 12pt !important;
    grid-template-columns: repeat(3, 1fr);
    column-gap: 14pt;
    row-gap: 8pt;
  }
  .cred      { padding-top: 6pt; break-inside: avoid; }
  .cred-head { font-size: 10.5pt; }
  .cred-sub  { font-size: 8.5pt; color: #333; }

  .fees { padding: 14pt 0 0; }
  .fee-group + .fee-group { margin-top: 10pt; }
  .fee-group-title { font-size: 13pt !important; margin-bottom: 5pt; }

  .fee-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 6pt;
  }
  .fee-card,
  .fee-card + .fee-card { margin-top: 0; }
  .fee-card {
    padding: 8pt;
    gap: 4pt;
    break-inside: avoid;
    /* The screen border is too pale on a laser printout; match the notes. */
    border-color: #999;
  }
  /* Two fee groups no longer fit one sheet with the price on its own line,
     so on paper the price sits on the name's line, at its right edge. */
  .fee-card  { grid-template-columns: minmax(0, 1fr) auto; column-gap: 6pt; align-items: baseline; }
  .fee-name  { font-size: 12pt !important; }
  .fee-price { font-size: 12pt !important; color: #000; margin-top: 0; text-align: right; }
  .fee-desc  { grid-column: 1 / -1; }
  .fee-unit  { color: #333; font-size: 8pt; }
  .fee-desc  { font-size: 8.5pt; color: #000; }
  /* The travel caption, unruled, kept on the same page as the cards it
     qualifies. */
  .fee-note  { font-size: 8.5pt; line-height: 1.35; color: #333; margin-top: 3pt; break-before: avoid; }

  .band { padding: 12pt 0 0; gap: 6pt 20pt; break-inside: avoid; }
  .band-label { flex-basis: 90pt; font-size: 8pt; color: #333; }
  .band-body--about { gap: 5pt; max-width: none; }
  .about-lead { font-size: 10.5pt; line-height: 1.45; color: #000; }
  .about-para { font-size: 9pt; line-height: 1.45; color: #000; }
  .about-room { font-size: 10.5pt; line-height: 1.35; color: #000; margin: 0; }
  .about-room::before { display: none; }

  .contact {
    margin-top: 14pt;
    padding: 12pt 0 0;
    border-top: 1px solid #999;
  }
  .contact-title {
    font-size: 15pt !important;
    line-height: 1.1 !important;
    max-width: none;
  }
  /* 9.5pt keeps the sub on one line. At 10pt "around." wrapped alone, and
     that line pushed the footer onto a second page once the teacher
     credential's sub took two lines (PBIS moved into it, 2026-11-11). */
  .contact-sub   { font-size: 9.5pt; margin-top: 6pt; color: #000; max-width: none; }
  .contact-note  { font-size: 8.5pt; margin-top: 3pt; color: #333; max-width: none; }
  .contact-email { font-size: 12pt !important; margin-top: 8pt; }
  .contact-email a { color: #000; border-bottom: 1px solid #000; }

  .site-footer {
    padding: 10pt 0 0;
    font-size: 8.5pt;
    color: #000;
  }
  .site-footer a { color: #000; padding-block: 0; }

  a[href^="mailto"] { text-decoration: none; }

  /* Field edition: ink on paper. No coloured grounds, no accent. */
  .site-header,
  .panel:has(> .site-header) { border-bottom: 0; }
  .hero,
  .hero-sub,
  .cred-head,
  .cred-sub,
  .contact,
  .contact-sub,
  .contact-note,
  .fee-group-title,
  .band-label { color: #000; }
  .hero-rule { background: #000; width: 44px; }
  .cred { border-top-color: #999; }
  .site-footer { background: transparent; border-top: 0; padding-inline: 0; }
}
