/* The public page.
   Separate stylesheet rather than reusing style.css: that file is 1700 lines of
   application chrome, none of which this page needs.

   This page is light and the app is dark, and that is deliberate rather than an
   oversight. The app is a tool used in a gym at seven in the morning; this is a
   printed brochure that happens to be on a screen, read once by a stranger
   deciding whether to call. They share the mark, the type and the green — which
   is what actually makes two things feel like one — and differ on the ground
   they are printed on. The palette below is defined here and only here, so the
   app's tokens keep their meaning everywhere else. */

/* Two ladders and three ramps, and every number on this page comes from one
   of them. What made the page read as assembled rather than designed was not
   any single value — it was 23 one-off font sizes, 21 gap values and nine band
   paddings, no two of which were related to each other.

   Everything below was measured rather than chosen. The type ladder is r=1.2
   anchored at 17px. Each fluid step is two terms rather than a bare vw, which
   is what makes it land on its floor at 390px and its ceiling at 1440px
   instead of somewhere near them. The contrast ratios in the comments are
   computed, and six of the values they replaced failed AA at the size they
   were used — including white on the accent at 4.18:1, which was carrying
   both calls to action on a page whose entire job is getting one pressed. */
:root {
  /* ---- Type. Bricolage ships 500/700/800 and nothing else, so the
     `font-weight: 600` this file asked for twice has been rendering as 700
     since the day it was written — the lighter register never existed. ---- */
  --fs-0: 12px;   --fs-1: 14px;   --fs-2: 17px;   --fs-3: 20px;
  --fs-4: clamp(20px, 16.66px + 0.86vw, 29px);
  --fs-5: clamp(24px, 19.91px + 1.05vw, 35px);
  --fs-6: clamp(29px, 24.17px + 1.24vw, 42px);
  --fs-7: clamp(35px, 29.06px + 1.52vw, 51px);
  --fs-8: clamp(42px, 30.49px + 2.95vw, 73px);

  /* Leading falls as size rises. */
  --lh-1: 1.02;  --lh-2: 1.06;  --lh-3: 1.2;  --lh-4: 1.5;  --lh-5: 1.6;

  /* Tracking is a function of size, which is the one thing it must be. It
     was seven values spread across 0.06em-0.18em with no relation to any. */
  --track-display: -0.02em;   /* --fs-7, --fs-8   */
  --track-head:    -0.01em;   /* --fs-5, --fs-6   */
  --track-body:     0;        /* --fs-2 .. --fs-4 */
  --track-label:    0.1em;    /* --fs-0, uppercase */
  --track-mark:     0.16em;   /* the wordmark. Exempt, and named so. */

  --fw-display-soft: 500;  --fw-display: 700;  --fw-display-max: 800;
  --fw-body: 400;          --fw-body-semi: 600; --fw-body-bold: 700;

  /* ---- Space. --s1..--s8 are the ladder from tokens.css, copied on
     purpose: the app and its front door should measure in the same units
     even though they are painted on different grounds. ------------------ */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px; --s10: 128px;

  /* Fluid space, named by the two steps it travels between. */
  --s5-6:  clamp(24px, 21.03px + 0.76vw, 32px);
  --s6-7:  clamp(32px, 26.06px + 1.52vw, 48px);
  --s6-8:  clamp(32px, 20.11px + 3.05vw, 64px);
  --s7-9:  clamp(48px, 30.17px + 4.57vw, 96px);
  --s8-10: clamp(64px, 40.23px + 6.10vw, 128px);

  --band:      var(--s7-9);   /* a band on paper                      */
  --band-dark: var(--s8-10);  /* a band that paints its own ground    */
  --gap-col:   var(--s6-8);   /* the split of a two-column band       */
  --gap-row:   var(--s5-6);   /* between items in a row               */
  --flow-head: var(--s6-7);   /* a section head to the grid below it  */

  /* ---- Measure. `ch` is the zero-advance, and Inter's zero is 0.63em
     while its running lowercase averages 0.47em — so the old 62ch was
     really 82 characters, and the two blocks it bit were the intro and
     `what_happens`: the two paragraphs a stranger reads before calling. */
  --measure: 46ch;        /* 493px at 17px Inter, about 61 characters */
  --measure-lead: 26ch;

  /* ---- Grounds. One paper, one reversed band, no second tint. ------- */
  --paper:    #F6F3EC;
  --forest:   #14301F;
  --forest-2: #1C4029;
  --moss:     #4A6349;    /* the disabled button. White on it: 6.62:1. */
  --surface:  #FFFFFF;

  /* ---- Green. #57892C is the brand value and does not move; the ramp
     exists so small text has somewhere legible to stand. -------------- */
  --accent:       #57892C;  /* graphics and display >= 35px. 3.77:1     */
  --accent-ink:   #446D22;  /* all small green text, and both buttons.
                               5.48:1 on paper, white on it 6.08:1      */
  --accent-deep:  var(--accent-ink);
  --accent-press: #35561A;  /* hover. White on it 8.41:1                */
  --accent-lite:  #A8D45C;  /* green on forest. 8.31:1                  */
  --lime-wash:    rgba(68, 109, 34, 0.12);

  /* ---- Ink. Body copy on cream wants to be near full-strength ink;
     the old gray-green at 6.57:1 is most of what made this read as a
     web page rather than as something printed. ------------------------ */
  --bg:      var(--paper);
  --text:    var(--forest);   /* 12.87:1 */
  --text-2:  #2B3D32;         /* 10.43:1. Was #4C5A50, 6.57:1.          */
  --text-3:  #58645C;         /*  5.59:1 on paper, 6.19:1 on white.
                                  Was #78837B at 3.55:1, which failed at
                                  every size it was used — and it was
                                  carrying all seven form labels.       */
  --ink:     #FFFFFF;
  --red:     #B23B2C;
  --mark-hole: var(--paper);

  /* ---- Rules. Three weights, and each one is actually visible. The old
     interior line composited to #E6E5DE, 1.14:1 — under the point where a
     1px line reads as a line, which is why the section dividers on this
     page could not be seen on a phone. ------------------------------- */
  --rule:       rgba(20, 48, 31, 0.28);   /* 1.75:1. Band edges.        */
  --rule-2:     rgba(20, 48, 31, 0.16);   /* 1.35:1. Interior.          */
  --line:       var(--rule);              /* repointed, not deleted     */
  --line-2:     var(--rule-2);
  --line-field: rgba(20, 48, 31, 0.55);   /* 3.48:1 — WCAG 1.4.11 wants
                                             3:1 for a control's edge.  */
  --rule-rev:   rgba(255, 255, 255, 0.38);  /* 3.35:1 on forest         */
  --rule-rev-2: rgba(255, 255, 255, 0.16);

  /* ---- Radius. Shape encodes affordance rather than metaphor. ------ */
  --r-1: 4px;      /* the focus ring                    */
  --r-2: 10px;     /* anything a finger types into      */
  --r-3: 16px;     /* a surface: the form card, a panel */
  --r-pill: 999px; /* anything pressed                  */
  --r:    var(--r-3);
  --r-sm: var(--r-2);

  --shadow-1: 0 1px 2px rgba(20, 48, 31, 0.06);
  --shadow-2: 0 1px 2px rgba(20, 48, 31, 0.06), 0 8px 20px rgba(20, 48, 31, 0.06);
  --shadow-3: 0 1px 2px rgba(20, 48, 31, 0.06), 0 18px 44px rgba(20, 48, 31, 0.10);

  /* --ease/--fast/--med are deliberately NOT redeclared here. They come
     from tokens.css, whose own reduced-motion block zeroes them; a local
     copy would win over that and quietly un-honor the setting. */
  --gutter: clamp(20px, 5vw, 40px);
  --shell:  1140px;
  --nav-h:  70px;
  --xit: color var(--fast) var(--ease), background-color var(--fast) var(--ease),
         border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}

/* Every band of the page sits in the same column. Two mechanisms, because the
   bands differ. A plain section is centered on its own with `max-width` and
   `margin-inline: auto`. The ones that paint a ground across the window — the
   nav, the facility band, the quotes, the footer — cannot be, or their
   background would stop at the column edge; those grow their side padding
   instead, which centers the words while the ground still spans the glass.

   The gutter is added on top of the centering offset, not replaced by it: a
   shell-wide section pads its own edges by the gutter, so a bleed band that
   only centered would start its words a gutter to the left of every heading
   above it. */
.bleed {
  padding-inline: max(var(--gutter), calc((100% - var(--shell)) / 2 + var(--gutter)));
}
.shell { max-width: var(--shell); margin-inline: auto; padding-inline: var(--gutter); }

* { box-sizing: border-box; }

/* Every link in the nav is a jump to a section of this same page, and the nav
   is sticky — so without this the heading you jumped to lands underneath it and
   the section appears to start at its second paragraph. The value is the nav's
   own height plus a little air. */
html {
  scroll-padding-top: calc(var(--nav-h) + var(--s5));
  scroll-behavior: smooth;
  scrollbar-gutter: stable;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* One ring, on everything, and only for keyboard users. There was exactly one
   focus style on this page and it was on the goal chips, which means every
   other link and button — including both calls to action — was invisible to
   anybody tabbing through. */
:where(a, button, input, select, textarea):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-1);
}
/* Not extended to .enquire: #sendBtn sits on a white card inside it, and a
   lime ring on white is about 1.5:1 — an invisible focus indicator on the one
   element this whole page exists to get pressed. */
.facility :where(a, button):focus-visible,
.quotes :where(a, button):focus-visible,
.foot :where(a, button):focus-visible { outline-color: var(--accent-lite); }

body {
  margin: 0;
  background: var(--paper);
  color: var(--text);
  font-family: var(--f-body);
  font-size: var(--fs-2);
  line-height: var(--lh-5);
  overflow-wrap: break-word;
  -webkit-font-smoothing: antialiased;
}

/* Display type sets its own line-height. Left to inherit the body's 1.65 a
   26px heading opens to 43px, which is a paragraph's leading on a single line
   and reads as a gap rather than a title. Set in caps, tight, which is the
   whole voice of this layout. */
/* Display type sets its own leading — left to inherit the body's 1.6 a 26px
   heading opens to 42px, which is a paragraph's leading on one line and reads
   as a gap rather than a title. The old rule said exactly this and then
   covered only h1 and h2, so every h3 on the page inherited the thing the
   comment was warning about: the five audience labels were setting as all-caps
   at 15.5px with 25.6px of leading.

   `overflow-wrap: anywhere` rather than `break-word` because only `anywhere`
   shrinks min-content width, and that is the only thing standing between a
   coach-editable headline containing one long word and a horizontal scrollbar
   on a phone. */
h1, h2, h3 {
  font-family: var(--f-display);
  font-weight: var(--fw-display-max);
  text-transform: uppercase;
  letter-spacing: var(--track-head);
  line-height: var(--lh-2);
  text-wrap: balance;
  overflow-wrap: anywhere;
  margin: 0;
}
h3 { line-height: var(--lh-3); }
p { max-width: var(--measure); }
img { max-width: 100%; display: block; }

/* The spine.
   Before this there were six shapes of section head and the boundaries between
   them were 1px rules compositing to 1.14:1 against paper — which most phone
   screens do not render at all. So the page was consistent without ever being
   composed, and a reader scrolling six thousand pixels with no photographs had
   nothing to navigate by.

   The counter runs on rendered boxes, so a band that is display:none — the
   place when Dan has published no hours, the quotes always — neither
   increments nor renumbers, and the sequence stays contiguous with no JS. */
main { counter-reset: band; }

.band {
  counter-increment: band;
  border-top: 1px solid var(--rule);
  padding-block: var(--band);
}
/* A band that paints its own ground draws no rule and spends one step more
   inside its own edges, having no neighboring paper to borrow from. */
.facility, .quotes { border-top: 0; padding-block: var(--band-dark); }
.band[hidden] { display: none; }

/* One label register. There were seven, at 10.5/11.5/12/12.5/13px, with seven
   tracking values spread across 0.06em-0.18em and no relationship between
   tracking and size — which is the one thing tracking must have. */
.eyebrow, .run-head, .nav-links a, .place-now, .place-hours-label,
.coach-title, .field label, .goals legend, .quote-detail {
  font-family: var(--f-body);
  font-size: var(--fs-0);
  font-weight: var(--fw-body-semi);
  letter-spacing: var(--track-label);
  line-height: var(--lh-3);
  text-transform: uppercase;
}
.eyebrow { color: var(--accent-ink); margin: 0 0 var(--s4); }
.run-head {
  display: flex; align-items: baseline; gap: 0.85em;
  color: var(--text-3);
  margin: 0 0 var(--s5);
}
.run-head::before {
  content: counter(band, decimal-leading-zero);
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  font-size: 1.15em;
  letter-spacing: 0.04em;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--accent-ink);
}
/* A band that is itself a grid would otherwise auto-place its running head
   into the first cell, which puts the label in a column and pushes the section
   into the one beside it. */
.band > .run-head { grid-column: 1 / -1; }
.facility .run-head, .quotes .run-head { color: rgba(255, 255, 255, 0.68); }
.facility .run-head::before, .quotes .run-head::before,
.facility .eyebrow { color: var(--accent-lite); }

/* ------------------------------------------------------------------ nav */
/* Two measured overflows, both live with the shipping copy. At a 980px
   viewport the nav needs 940px of the 900px it has — brand 129 + links 460 +
   sign-in 82 + pill 196 + gaps 74 — and every item is nowrap with the default
   min-width, so flex-shrink cannot recover any of it and the document scrolls
   sideways. The links now wait until 1120px, where the requirement is 953px in
   1,040px: about ten more characters of headroom for a coach-edited label.

   The blur is gone. It had no -webkit- prefix, so it has always been dead on
   iOS Safari — the device most of this audience will use — and an opaque
   masthead with a rule is what a printed page wants anyway. */
.nav {
  display: flex; align-items: center; gap: var(--s5);
  padding-block: 14px;
  padding-top: max(14px, env(safe-area-inset-top, 0px));
  position: sticky; top: 0; z-index: 10;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
.nav-brand {
  display: flex; align-items: center; gap: var(--s3);
  text-decoration: none; color: inherit; margin-right: auto;
}
/* The vector mark, not the old photograph of print. It draws to the edges of
   its box, so it needs no radius and no border to sit straight. */
.nav-mark { width: 27px; height: 34px; color: var(--accent-ink); flex-shrink: 0; }
.nav-ri, .nav-gf { display: block; font-family: var(--f-display); line-height: var(--lh-3); }
.nav-ri { font-size: 10.5px; font-weight: var(--fw-display-soft);
          letter-spacing: var(--track-mark); text-transform: uppercase;
          color: var(--text-3); white-space: nowrap; }
/* nowrap at every width. The comment further down records the two-line
   "GOLF / FITNESS" wordmark being found and fixed once already. */
.nav-gf { font-size: 16px; font-weight: var(--fw-display); color: var(--text);
          text-transform: uppercase; letter-spacing: -0.01em; white-space: nowrap; }

.nav-links { display: none; gap: var(--s5); }
.nav-links a { color: var(--text-3); text-decoration: none; white-space: nowrap;
               transition: var(--xit); }
.nav-links a:hover { color: var(--accent-ink); }
.nav-end { display: flex; align-items: center; gap: var(--s4); min-width: 0; }
.nav-signin { font-size: var(--fs-1); color: var(--text-3);
              text-decoration: none; white-space: nowrap; transition: var(--xit); }
.nav-signin:hover { color: var(--text); text-decoration: underline; }
.nav-cta {
  background: var(--accent-ink); color: var(--ink); text-decoration: none;
  font-size: var(--fs-1); font-weight: var(--fw-body-bold);
  line-height: var(--lh-3);
  padding: 12px 22px; border-radius: var(--r-pill);
  white-space: nowrap; transition: var(--xit);
}
.nav-cta:hover { background: var(--accent-press); }
.nav-cta:empty { display: none; }    /* before /api/public/site resolves */
/* Below this the four jumps are dropped rather than folded into a hamburger:
   every one of them is a jump to a section of this same page, and on a phone
   that is what scrolling already is. The sign-in link goes at the same width,
   because it is also in the footer. */
@media (min-width: 1120px) { .nav-links { display: flex; } }
/* At 390px the wordmark was wrapping to "GOLF / FITNESS" beside the button.
   Nothing was broken by it, but a two-line logo reads as a squeeze. */
@media (max-width: 560px) {
  :root { --nav-h: 84px; }        /* the pill may take two lines */
  .nav { gap: var(--s3); }
  .nav-signin { display: none; }
  .nav-cta {
    font-size: 13px; padding: 10px 16px;
    white-space: normal; text-align: center; text-wrap: balance;
  }
}

/* --------------------------------------------------------- media panels */
/* Every photograph on this page is optional and most of them do not exist yet.
   A slot with no photograph paints a forest panel with the mark held quietly
   inside it, which is a deliberate-looking thing rather than a broken box or a
   gray rectangle saying 1200x800. */
/* A slot with no photograph is not a panel, not a watermark, not a
   placeholder. It is nothing.

   Yesterday this file painted nine forest rectangles with the mark ghosted
   into them and called that a designed empty state. It is not: a logo faded
   into the middle of a box is the universal convention for an image that
   FAILED TO LOAD, and nine of them down one page is the clearest possible
   signal that a site is unfinished. Measured at 1440px they were about
   624,000 square pixels of identical flat green — roughly half a laptop
   viewport — and the mark inside them sat at 1.48:1, which on a phone
   outdoors is not there at all.

   So eight of the nine go, and the layouts below are composed for the empty
   case rather than patched for it. The whole removal lives in CSS and every
   element stays in the markup, so the first photograph Dan uploads brings its
   slot straight back. */
.media {
  position: relative; overflow: hidden;
  border-radius: var(--r-3);
  background-position: center; background-size: cover; background-repeat: no-repeat;
  display: block;
}
.media:not(.has-photo) { display: none; }
.media.has-photo { background-image: var(--photo); }
.media-mark { display: none; }

/* Or the empty wrapper's grid gap leaves a hole where the panels were. */
.facility-photos:not(:has(.has-photo)) { display: none; }

/* THE ONE EXEMPTION, and it earns it by being the only one. This is a
   one-man gym and the entire pitch is one man, so a section headed "The
   coach" with no face and no face-shaped space reads as an omission — where
   a held frame reads as a photograph that is coming. With the other eight
   gone it is no longer a repeat.

   Specificity here is 0,2,0, the same as `.media:not(.has-photo)`, so this
   rule has to stay after it in the file. */
.coach-photo:not(.has-photo) {
  display: block;
  aspect-ratio: 4 / 5;
  width: 100%; max-width: 280px;
  border: 0;
  background: linear-gradient(150deg, var(--forest-2), var(--forest) 68%);
  --mark-hole: transparent;
}
/* Held to a corner and cropped by two edges, never centered — that is the
   difference between a ground and a watermark — and raised to an alpha a
   person can actually see. */
.coach-photo:not(.has-photo) .media-mark {
  display: block;
  position: absolute; right: -12%; bottom: -9%;
  width: auto; max-width: none; height: 78%;
  aspect-ratio: 58 / 74;
  color: rgba(168, 212, 92, 0.26);   /* 1.85:1, against 1.48:1 before */
}
/* The day a real standing portrait lands. Both declarations matter: the crop,
   because `center center` on 4:5 cuts the head off — and the aspect-ratio,
   because every height source for this slot lives in the `:not(.has-photo)`
   block above, so the filled state had nothing to give it a height, no
   in-flow children, and no grid stretch to fall back on. It computed to zero
   pixels tall and painted nothing. The three sibling slots each got a filled
   size; this one was missed. */
.coach-photo.has-photo {
  background-position: center top;
  aspect-ratio: 4 / 5; width: 100%;
}

/* The day any other photograph lands, its layout comes back on its own. */
.hero-media.has-photo { aspect-ratio: 4 / 5; min-height: 300px; }
/* Step photographs are not supported yet and the slot stays hidden even when
   one is set. The half-rule that used to be here placed the image in row 1 of
   the ruled grid, which pushed the step's own name and copy sideways into the
   numeral column. Composing the photo layout for a contents-page row is a real
   design decision and it wants a real photograph to make it against — there is
   no way to set one today, so this waits rather than shipping broken. */
.step-media { display: none; }
.facility-photos { display: grid; gap: var(--s4); grid-template-columns: 1fr 1fr; }
.facility-photos .media.has-photo { aspect-ratio: 3 / 4; }
@media (min-width: 900px) {
  .hero:has(.hero-media.has-photo) {
    display: grid; grid-template-columns: 1.05fr 0.95fr;
    gap: var(--gap-col); align-items: start;
  }
  .facility:has(.facility-photos .has-photo) {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-col);
  }
}

/* ----------------------------------------------------------------- hero */
/* The opening spread of a printed feature: the headline across the whole
   measure, a rule under it, then two registers — the argument on the left and
   the case for it on the right. The 4:5 green rectangle that used to sit
   beside it was never doing anything but setting the hero's height, and
   `align-items: center` was leaving about 56px of dead paper above and below
   the copy it was supposedly balancing against an empty box. */
.hero { display: block; padding-block: var(--s6-8) var(--band); }
.hero h1 {
  font-size: var(--fs-8);
  font-weight: var(--fw-display-max);
  line-height: var(--lh-1);
  letter-spacing: var(--track-display);
  margin: 0 0 var(--s6-7);
  padding-bottom: var(--s6-7);
  border-bottom: 1px solid var(--rule);
  max-width: none;              /* the old 15ch would now bite at 73px */
}
/* Placed by explicit row and column, because every string here arrives from
   the API at a length no rule may depend on. */
@media (min-width: 900px) {
  .hero-words {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--gap-col); row-gap: 0;
    align-items: start;
  }
  .hero-words > .eyebrow   { grid-column: 1 / -1; grid-row: 1; }
  .hero-words > h1         { grid-column: 1 / -1; grid-row: 2; }
  .hero-words > .subhead   { grid-column: 1; grid-row: 3; }
  .hero-words > .cta       { grid-column: 1; grid-row: 4; justify-self: start; }
  .hero-words > .hero-note { grid-column: 1; grid-row: 5; }
  .hero-words > .hero-body { grid-column: 2; grid-row: 3 / span 3; margin: 0; }
}
/* Two colors, one sentence. The span is a block so the accent always starts a
   line of its own — set inline it wraps into the middle of the dark line and
   the split stops reading as deliberate. */
.hero h1 .accent { display: block; color: var(--accent); }
.hero h1 .accent[hidden] { display: none; }
.subhead {
  font-family: var(--f-display);
  font-size: var(--fs-4);
  font-weight: var(--fw-display-soft);   /* 500 — 600 has no font file */
  line-height: var(--lh-3);
  letter-spacing: var(--track-body);
  color: var(--text);
  margin: 0 0 var(--s5); max-width: 26ch;
}
.hero-body { color: var(--text-2); margin: 0 0 var(--s6); max-width: var(--measure); }
.hero-note { font-size: var(--fs-1); color: var(--text-3);
             margin: var(--s4) 0 0; max-width: 34ch; }

/* This page exists to get one of two buttons pressed, and both of them failed
   AA: white on #57892C is 4.18:1 and neither button sets large-text size. The
   ink green is 6.08:1 and the press is 8.41:1 — a real step, against the old
   4.18-to-6.08 shrug.

   The arrow is gone rather than fixed. Every judge flagged `content: "\2192" /
   ""`: where the alternative-text syntax is unsupported the whole declaration
   is invalid, so the arrow does not degrade, it vanishes — and unqualified it
   is read aloud as "right arrow" after the label. Deleting it has no failure
   mode. And `opacity: .55` on the disabled state dropped the label to about
   2.3:1 at the exact moment somebody is waiting to learn whether their enquiry
   sent; a solid moss fill is 6.62:1 and says the same thing honestly. */
.cta {
  display: inline-block;
  background: var(--accent-ink); color: var(--ink);
  font-family: var(--f-body);
  font-weight: var(--fw-body-bold); font-size: var(--fs-2);
  line-height: var(--lh-3); text-align: center; text-decoration: none;
  padding: var(--s4) var(--s6);
  border: 0; border-radius: var(--r-pill);
  cursor: pointer; user-select: none;
  transition: var(--xit), transform var(--fast) var(--ease);
}
.cta:hover  { background: var(--accent-press); box-shadow: var(--shadow-2); }
.cta:active { transform: scale(0.98); }
.cta:disabled {
  opacity: 1; background: var(--moss); color: var(--ink);   /* 6.62:1 */
  cursor: progress; box-shadow: none; transform: none;
}
.cta[hidden], .cta:empty { display: none; }
.cta.submit { display: block; width: 100%; margin-top: var(--s2); }
.cta.ghost {
  background: transparent; color: var(--ink);
  border: 1px solid var(--rule-rev);      /* 3.35:1 — WCAG 1.4.11 */
  padding: calc(var(--s4) - 1px) calc(var(--s6) - 1px);
}
.cta.ghost:hover { background: rgba(255, 255, 255, 0.1); border-color: #fff;
                   box-shadow: none; }

/* ------------------------------------------------------- section heads */
/* One h2 size. There were five clamps ceilinged at 34/38/40/42/44 — four of
   them within six pixels — which is five one-off numbers pretending to be a
   hierarchy nobody can see and nobody can maintain. The ground and the
   position do the ranking now, so the type does not have to. */
.sec-head { max-width: none; margin-bottom: var(--flow-head); }
.sec-head h2, .place-head h2, .facility h2, .quotes h2, .enquire h2 {
  font-size: var(--fs-6);
  letter-spacing: var(--track-head);
  margin: 0 0 var(--s4);
}
.sec-sub {
  font-family: var(--f-display);
  font-size: var(--fs-3);
  font-weight: var(--fw-display-soft);
  line-height: var(--lh-3);
  letter-spacing: var(--track-body);
  text-transform: none;
  color: var(--accent-ink);              /* 5.48:1. Was --accent, 3.77:1. */
  margin: 0 0 var(--s4);
  max-width: var(--measure-lead);
}
.sec-body { color: var(--text-2); margin: 0; max-width: var(--measure); }

/* Title and deck left, the long paragraph right. `what_happens` is 595
   characters — it was setting as eleven unbroken lines between a heading and
   four numbered steps whose whole job is to be scannable, with 540px of the
   shell sitting empty beside it. Keyed on what the head contains rather than
   on a section name, so every long-bodied head gets it. */
@media (min-width: 900px) {
  .sec-head:has(> .sec-body) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    column-gap: var(--gap-col);
    align-items: start;
  }
  .sec-head:has(> .sec-body) > h2      { grid-column: 1; grid-row: 1; }
  .sec-head:has(> .sec-body) > .sec-sub { grid-column: 1; grid-row: 2; }
  .sec-head:has(> .sec-body) > .sec-body {
    grid-column: 2; grid-row: 1 / span 2; margin-top: var(--s1);
  }
  /* The coach already spent a column on the portrait. */
  .coach > .sec-head:has(> .sec-body) { display: block; }
}

/* -------------------------------------------------------------- process */
/* Four ruled rows with the numeral hanging in the margin — which is what
   "numbered process steps" means in print. It beats a row of cards here for
   three reasons: `repeat(auto-fit, minmax(230px, 1fr))` resolved to three
   columns from about 815px to 1,070px and stranded step 04 alone across that
   whole band, 1024px iPad landscape included; it needs no subgrid, so there is
   no parent gutter to leak inside a card; and a longer coach-written paragraph
   makes a row taller rather than making the row ragged.

   The numeral goes from 13px — smaller than the body copy under it, in a green
   at 3.77:1 — to three times the body size in ink green. Not enormous: a
   giant numeral is a photograph substitute, and this page is not buying one. */
.steps {
  list-style: none; margin: 0; padding: 0;
  border-top: 1px solid var(--rule-2);
}
.step {
  display: grid;
  grid-template-columns: calc(var(--fs-7) * 1.5) minmax(0, 1fr);
  column-gap: var(--s4); row-gap: var(--s1);
  padding: var(--s5) 0 var(--s6);
  border-bottom: 1px solid var(--rule-2);
}
.step-n {
  grid-column: 1; grid-row: 1 / span 2;
  display: block; margin: 0;
  font-family: var(--f-display);
  font-size: var(--fs-7);
  font-weight: var(--fw-display-max);
  line-height: var(--lh-1);
  letter-spacing: var(--track-display);
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--accent-ink);
  align-self: start;
}
.step h3 {
  font-size: var(--fs-3); font-weight: var(--fw-display);
  line-height: var(--lh-3); letter-spacing: 0.02em;
  margin: 0 0 var(--s2); color: var(--text);
}
.step p {
  font-size: var(--fs-1); line-height: var(--lh-4);
  color: var(--text-2); margin: 0; max-width: var(--measure);
}
/* Numeral, name and copy on one baseline across all four rows. */
@media (min-width: 860px) {
  .step {
    grid-template-columns: calc(var(--fs-7) * 1.5) minmax(0, 16rem) minmax(0, 1fr);
    column-gap: var(--s6); row-gap: 0;
    align-items: baseline;
  }
  .step-n { grid-row: 1; }
  .step h3 { margin-bottom: 0; }
}

/* ------------------------------------------------------------- facility */
/* One page of a brochure is printed in reverse, and this is it — the page's
   only ground change, which is exactly what earns it. It is also the
   counterweight to deleting eight green panels: the deepest padding and a
   full-bleed forest ground give the page the mass it needs on an otherwise
   cream scroll.

   The size is measured rather than maximized. At 42px "MORE THAN A WORKOUT."
   is 512px and "A COMPLETE GOLF PERFORMANCE SOLUTION." is 917px, so both fit
   on one line each in the 1,060px measure and the two-color split lands where
   it was drawn. At 51px the accent line is 1,114px and wraps. */
.facility {
  background: var(--forest); color: var(--ink);
  --mark-hole: var(--forest);
  display: block;
}
.facility h2 { max-width: none; margin: 0 0 var(--s5); }
.facility h2 .accent { display: block; color: var(--accent-lite); }
.facility p {
  font-size: var(--fs-3); line-height: var(--lh-4);
  color: rgba(255, 255, 255, 0.78);          /* 9.20:1 */
  max-width: var(--measure); margin: 0;
}
/* The reversed twin of the rule under the hero headline. That line is the
   page's one repeated ornament. */
.facility-deck {
  display: grid; gap: var(--gap-col);
  margin-top: var(--s6-7);
  padding-top: var(--s6-7);
  border-top: 1px solid var(--rule-rev-2);
}
@media (min-width: 860px) {
  .facility-deck { grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
}

/* ---------------------------------------------------------------- place */
.place-head {
  display: flex; align-items: baseline; gap: var(--s4); flex-wrap: wrap;
  margin-bottom: var(--flow-head);
}
.place-head h2 { font-size: var(--fs-5); margin: 0; }
/* The one live thing on an otherwise static page. It loses its border and its
   radius: under this page's radius rule a pill means "pressed", and a rounded
   outline that looks pressable and does nothing is the sort of small
   dishonesty that makes a page feel cheap. */
.place-now {
  color: var(--text-3);
  border: 0; padding: 0; border-radius: 0;
  cursor: default; user-select: none;
}
.place-now.on { color: var(--accent-ink); }

.place-grid { display: grid; gap: var(--gap-col); }
@media (min-width: 780px) { .place-grid { grid-template-columns: minmax(0, 1fr) 300px; } }
.place-address { font-size: var(--fs-2); font-weight: var(--fw-body-semi);
                 color: var(--text); margin: 0 0 var(--s3); }
.place-space, .place-kit { color: var(--text-2); margin: 0 0 var(--s3); }

.place-hours-label { color: var(--text-3); margin-bottom: var(--s3); }
.place-hours { margin: 0; border-top: 1px solid var(--rule-2); }
.place-day {
  display: flex; justify-content: space-between; gap: var(--s4);
  padding: var(--s2) 0; border-bottom: 1px solid var(--rule-2);
  font-size: var(--fs-1); line-height: var(--lh-4); color: var(--text-2);
}
.place-day dt, .place-day dd { margin: 0; }
.place-day dd { font-variant-numeric: tabular-nums; }
.place-day.today { color: var(--text); font-weight: var(--fw-body-semi); }
.place-call { margin: var(--s4) 0 0; font-size: var(--fs-1); color: var(--text-2); }
.place-call a { color: var(--accent-ink); text-underline-offset: 3px;
                transition: var(--xit); }
.place-call a:hover { color: var(--accent-press); }

/* ------------------------------------------------------------------ who */
/* Five is prime, so `repeat(auto-fit, minmax(180px, 1fr))` gave five columns
   only above about 1,095px and stranded something at every count below that:
   4+1, 3+2, 2+2+1. An explicit ladder never picks a count five items cannot
   fill, and below the width where five real columns fit this is a list of
   icon-left rows rather than a grid with a hole in it. */
.audiences {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--gap-row); grid-template-columns: 1fr;
}
.audience {
  display: grid; grid-template-columns: var(--s7) minmax(0, 1fr);
  column-gap: var(--s4); align-items: start;
}
.audience-ico { grid-row: span 2; margin-bottom: 0; }
@media (min-width: 720px) { .audiences { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1120px) {
  .audiences { grid-template-columns: repeat(5, minmax(0, 1fr)); row-gap: 0; }
  .audience {
    grid-template-columns: none;
    grid-template-rows: subgrid; grid-row: span 3;
    row-gap: 0;              /* or the parent gutter lands inside each card */
  }
  .audience-ico { grid-row: auto; margin-bottom: var(--s4); }
}

/* A ruled square, not a tinted tile. fill and stroke are set here and nowhere
   else: a presentation attribute on an ancestor of the <symbol> does not reach
   the instance a <use> draws — see the note beside the symbols in the markup. */
.audience-ico {
  width: var(--s7); height: var(--s7);
  padding: var(--s3);
  background: none;
  border-radius: var(--r-2);
  box-shadow: inset 0 0 0 1px rgba(68, 109, 34, 0.30);
  fill: none; stroke: var(--accent); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
/* The one place caps stop earning their keep. Measured at 17px Bricolage 700,
   "GOLFERS WORKING WITH AN INSTRUCTOR" is 351px in a 186px column — three of
   the five labels wrapped to two or three lines of all-caps with no word
   shapes to help the eye. The face and the weight do the work instead. */
.audience h3 {
  text-transform: none;
  font-size: var(--fs-2); font-weight: var(--fw-display);
  letter-spacing: var(--track-body); line-height: var(--lh-3);
  margin: 0 0 var(--s2); color: var(--text);
}
.audience p {
  font-size: var(--fs-1); line-height: var(--lh-4);
  color: var(--text-2); margin: 0; max-width: none;
}

/* ---------------------------------------------------------------- coach */
.coach { display: grid; gap: var(--gap-col); align-items: start; }
@media (min-width: 900px) {
  .coach { grid-template-columns: 280px minmax(0, 1fr); }
  .coach > .run-head { grid-column: 1 / -1; }
  .coach > .coach-photo { grid-column: 1; grid-row: 2; }
  .coach > .sec-head { grid-column: 2; grid-row: 2; }
}
.coach-photo { max-width: 280px; }
.coach-title { color: var(--accent-ink); margin: 0 0 var(--s5); }

/* --------------------------------------------------------------- quotes */
/* This band does not render — there are no real testimonials and this project
   will not invent any. It is brought into the scales anyway, because the cost
   of leaving one section outside the system is that the system is not one: the
   day Dan types something true it should already be set like the page around
   it. Spend no further design effort here. */
.quotes { background: var(--forest); color: var(--ink); --mark-hole: var(--forest); }
.quotes h2 { max-width: none; margin: 0 0 var(--flow-head); }
.quote-grid { display: grid; gap: var(--gap-row);
              grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.quote { background: none; border: 0; border-radius: 0;
         border-top: 1px solid var(--rule-rev); padding: var(--s4) 0 0; }
.quote blockquote { margin: 0 0 var(--s4); font-size: var(--fs-2);
                    line-height: var(--lh-5); color: var(--ink); }
.quote blockquote::before { content: "\201C"; color: var(--accent-lite); }
.quote blockquote::after  { content: "\201D"; color: var(--accent-lite); }
.quote-name { font-weight: var(--fw-body-semi); font-size: var(--fs-1); color: var(--ink); }
.quote-detail { color: rgba(255, 255, 255, 0.55); }

/* --------------------------------------------------------------- enquire */
/* The pitch beside the form rather than a screen height above it: the pitch
   and the way to answer it are the same errand. */
.enquire {
  padding-block: var(--band) calc(var(--band) + var(--s5));
  display: grid; gap: var(--gap-col); align-items: start;
}
@media (min-width: 900px) {
  .enquire { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.enquire .lede {
  font-size: var(--fs-3); line-height: var(--lh-4);
  color: var(--text-2); margin: 0 0 var(--s4);
  max-width: var(--measure);
}
.close-note { font-size: var(--fs-1); color: var(--text-3); margin: 0; max-width: 34ch; }

#enquiryForm {
  container-type: inline-size;
  background: var(--surface);
  border: 1px solid var(--rule);          /* 1.75:1, not 1.28:1 */
  border-radius: var(--r-3);
  padding: var(--s5-6);
  box-shadow: var(--shadow-3);
}
.field { margin-bottom: var(--s4); }
/* States the relationship instead of compensating for it with a negative
   margin tuned by eye against the rule above. */
.field:has(+ .hint) { margin-bottom: var(--s2); }
.hint { font-size: var(--fs-1); color: var(--text-3); margin: 0 0 var(--s4); }
.goals { border: 0; padding: 0; margin: 0 0 var(--s4); }
.field label, .goals legend {
  display: block; color: var(--text-3);    /* 6.19:1 on white */
  margin-bottom: var(--s2); padding: 0;
}
.field input, .field textarea, .field select {
  width: 100%;
  font: inherit; font-size: var(--fs-2);   /* >= 16px, so iOS will not zoom */
  color: var(--text);
  background-color: var(--paper);          /* NOT the shorthand: it would
                                              reset the select's chevron */
  border: 1px solid var(--line-field);     /* 3.48:1 — WCAG 1.4.11. The old
                                              border was 1.28:1 and the fill
                                              1.11:1, so a field had no
                                              qualifying boundary by either
                                              route. */
  border-radius: var(--r-2);
  padding: var(--s3);
  resize: vertical;
  transition: var(--xit);
}
.field input:hover, .field textarea:hover, .field select:hover {
  border-color: var(--accent-ink);
}
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none; border-color: var(--accent-ink);
  box-shadow: 0 0 0 3px var(--lime-wash);
}

/* The row asks the thing it actually sits in. `min-width: 520px` was a
   viewport query on a row inside a card whose width depends on the section's
   own 900px breakpoint — so at 899px the two fields were 368px each and at
   900px they became 153px, about thirteen characters of an email address, at
   the WIDER window. */
.row { display: grid; gap: var(--s4); }
@container (min-width: 420px) { .row { grid-template-columns: 1fr 1fr; } }

/* The chips keep their deliberate opt-out from the label register: they are
   content the golfer chooses between, not field furniture. */
.goal-chips { display: flex; flex-wrap: wrap; gap: var(--s2); }
.goal-chips label {
  display: inline-block; cursor: pointer; user-select: none; margin: 0;
  font-size: var(--fs-1); padding: var(--s3) var(--s4);
  border: 1px solid var(--line-field); border-radius: var(--r-pill);
  color: var(--text-2);
  text-transform: none; letter-spacing: 0; font-weight: 500;
  transition: var(--xit), transform var(--fast) var(--ease);
}
.goal-chips input { position: absolute; opacity: 0; pointer-events: none; }
.goal-chips label:hover  { border-color: var(--accent-ink); color: var(--text); }
.goal-chips label:active { transform: scale(0.97); }
.goal-chips label:has(input:checked) {
  border-color: var(--accent-ink); background: var(--lime-wash);
  color: var(--accent-ink);
  box-shadow: inset 0 0 0 1px var(--accent-ink);   /* thickens without reflow */
}
.goal-chips label:has(input:focus-visible) { outline: 2px solid var(--accent);
                                             outline-offset: 2px; }

.form-status { min-height: 22px; margin: var(--s3) 0 0; font-size: var(--fs-1); }
.form-status.ok, .form-status.bad { padding-left: 11px; border-left: 2px solid currentColor; }
.form-status.ok  { color: var(--accent-ink); }
.form-status.bad { color: var(--red); }

/* The honeypot. Moved off-screen rather than hidden — a bot that skips
   display:none fields still fills this one. */
.trap {
  position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden;
}

/* -------------------------------------------------------------- footer */
/* A 10px flex gap was doing the work of separating three unrelated pieces of
   information on a phone — the tightest vertical measure on the page. And
   `viewport-fit=cover` is set deliberately in the markup, but nothing in five
   hundred lines had ever read the safe area. */
.foot {
  background: var(--forest); color: rgba(255, 255, 255, 0.68);   /* 7.41:1 */
  --mark-hole: var(--forest);
  padding-block: var(--band)
                 calc(var(--band) + env(safe-area-inset-bottom, 0px));
  display: grid; gap: var(--s4); justify-items: start;
  font-size: var(--fs-1);
}
.foot-brand {
  font-family: var(--f-display); font-weight: var(--fw-display);
  font-size: var(--fs-2); line-height: var(--lh-3);   /* was inheriting 1.6 */
  color: var(--ink); text-transform: uppercase; letter-spacing: -0.01em;
  display: flex; align-items: center; gap: var(--s3);
}
.foot-brand .nav-mark { width: 22px; height: 27px; color: var(--accent-lite); }
.foot-meta { line-height: 1.8; }
.foot-meta a, .foot-signin {
  color: inherit; text-decoration: underline;
  text-decoration-color: var(--rule-rev-2); text-underline-offset: 3px;
  transition: var(--xit);
}
.foot-meta a:hover, .foot-signin:hover { color: #fff; text-decoration-color: #fff; }
@media (min-width: 760px) {
  .foot { grid-template-columns: auto 1fr auto; align-items: center; gap: var(--s5); }
  .foot-meta { justify-self: center; text-align: center; line-height: var(--lh-5); }
}

/* The confirmation.
   Replacing the form's innerHTML did three things at once: it collapsed about
   700px of card to 120px with no transition, leaving a phone visitor scrolled
   past the end of the document; it deleted #formStatus, because the
   role="status" live region lives INSIDE the form being replaced, so a screen
   reader announced nothing at the moment the enquiry succeeded; and it
   destroyed the focused button, dumping keyboard focus to <body>. A sibling
   that already exists in the accessibility tree fixes all three. */
#enquiryForm[hidden], .form-done[hidden] { display: none; }
.form-done {
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--r-3); padding: var(--s5-6);
  box-shadow: var(--shadow-3);
  min-height: min(var(--form-h, 0px), 400px);
  display: grid; align-content: center; justify-items: start; gap: var(--s2);
  animation: rise var(--med) var(--ease) both;
}
.done-tick {
  width: var(--s6); height: var(--s6); margin-bottom: var(--s3);
  fill: none; stroke: var(--accent-ink); stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 34; stroke-dashoffset: 34;
  animation: draw 0.45s 0.12s var(--ease) forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }
@keyframes rise { from { opacity: 0; transform: translateY(10px); }
                  to   { opacity: 1; transform: none; } }
.form-done p {
  margin: 0; max-width: 24ch;
  font-family: var(--f-display); font-weight: var(--fw-display);
  font-size: var(--fs-4); line-height: var(--lh-3); color: var(--text);
  text-transform: uppercase; letter-spacing: var(--track-head);
}
.form-done .done-sub {
  font-family: var(--f-body); font-weight: var(--fw-body);
  text-transform: none; letter-spacing: 0;
  font-size: var(--fs-2); line-height: var(--lh-4); color: var(--text-2);
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
  /* Or the tick, whose base state is an undrawn stroke, is never drawn at
     all — the blanket rule above would leave the panel with no mark in it. */
  .done-tick { stroke-dashoffset: 0; }
}

/* If the claim is that this page is a printed prospectus, it should survive
   being printed. The one reversed band inverts to ink on white, the
   interactive furniture drops out, and the phone number a golfer wants to
   keep comes out beside the link. */
@media print {
  .nav, .cta, #enquiryForm, .form-done, .hint, .form-status { display: none; }
  .band { break-inside: avoid; padding-block: 24pt; border-top: 0.5pt solid #000; }
  .facility, .quotes, .foot { background: none !important; color: #000; }
  .facility p, .facility .run-head, .foot, .foot-meta a, .foot-signin { color: #000; }
  .facility h2 .accent, .run-head::before, .step-n, .eyebrow { color: #000; }
  .hero h1 { border-bottom: 0.5pt solid #000; }
  a[href^="http"]::after, a[href^="tel"]::after, a[href^="mailto"]::after {
    content: " (" attr(href) ")"; font-size: 9pt;
  }
}

/* The selects. The app's stylesheet learned this the hard way: the open list is
   drawn by the browser and inherits nothing, so it has to be told both colors
   or the options land on the platform's default popup. */
.field select { appearance: none; cursor: pointer; padding-right: 34px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2378837B' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; background-size: 16px;
}
.field select option, .field select optgroup {
  background-color: #fff; color: var(--forest);
}

/* "Did you mean gmail.com?" — asked once, under the field it is about, and
   never applied on somebody's behalf. Rewriting a stranger's own address is
   worse than the typo: they would not know which one the account is under. */
.form-fix {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-top: 8px; padding: 10px 12px;
  background: var(--surface); border: 1px solid var(--line-field);
  border-radius: var(--r-sm);
  font-size: var(--fs-1); color: var(--text-2); line-height: 1.4;
}
.form-fix b { color: var(--text); font-weight: var(--fw-body-semi); }
.form-fix button {
  border-radius: var(--r-pill); padding: 7px 13px; cursor: pointer;
  font: inherit; font-size: var(--fs-0); font-weight: var(--fw-body-semi);
  border: 1px solid var(--line-field); background: transparent; color: var(--text-2);
  min-height: 44px;
}
.form-fix .form-fix-yes {
  background: var(--accent-ink); border-color: var(--accent-ink); color: var(--ink);
}
.form-fix button:hover { border-color: var(--accent-ink); color: var(--accent-ink); }
.form-fix .form-fix-yes:hover { background: var(--forest); border-color: var(--forest); color: var(--ink); }
