/* ==========================================================================
   Brand marks — and, by now, the site's global overrides.

   Linked from templates/layout.mjs, so it loads on EVERY page. It started as
   the header and footer logos, which is where the name comes from, and it has
   become the place a correction goes when it has to reach every page and the
   alternative is editing a rule buried in fifty kilobytes of jump.css. Each
   block below says what it overrides and why.

   Amy, September 21, 2026: "they need to be like 4X larger, think 1 inch on a
   desktop."

   One inch is the spec that can actually be checked, so that is what these
   numbers hit: 96px is one CSS inch by definition. Note that this is about
   1.4x the old header mark (70px), not 4x — 4x would be a 280px logo, taller
   than the whole header bar. If the intent really was 4x, the header has to be
   redesigned around it rather than just given a bigger number here.

   STEPPED, NOT SCALED. The first attempt used clamp(56px,6vw,96px), which
   sounds right and is wrong: 6vw only reaches 96px above about 1600px, so
   measured at 1280px the logo came out 77px and at 1024px 61px. Most actual
   desktops would not have got the inch. Breakpoints make "desktop" mean one
   size, which is what was asked for.

   THE OTHER HALF OF THE PROBLEM WAS RESOLUTION, and it was the real bug.
   build.mjs rewrites every <img> through the Netlify Image CDN using the
   tag's own width attribute as the requested width. Both logos carried
   width="88" left over from the old wide WordPress mark, so both were being
   fetched at 88 pixels wide no matter how large they were drawn — the header
   needed 93 device pixels and got 88, which is why they read soft. The
   attributes in layout.mjs now ask for 200 and 280, roughly 2x the drawn size,
   which is what a logo needs on a high-density screen.

   The attributes are square now as well. The files are 800x800, but the tags
   still declared 88x56 from the old logo, so the browser reserved a box of the
   wrong shape until the image loaded.
   ========================================================================== */

/* Phone first: the header bar has to stay usable next to the menu button. */
.header__logo img{height:58px;width:auto}
/* The mark is now the artwork itself, cropped tight (static/assets/img/
   jump-logo.png). The old file was an 800px square that was more than half
   empty canvas, so every height below drew a logo about 45% of that size:
   Randy, BugHerd #126, 10/16/2026, "It is very small on both currently."
   The heights stay roughly where they were and the logo fills them now. */
.footer__brand img{height:88px;width:auto;margin-bottom:1rem}

/* Tablet. */
@media (min-width:700px){
  .header__logo img{height:50px}
  .footer__brand img{height:112px}
}

/* Desktop — one inch. */
@media (min-width:1000px){
  .header__logo img{height:62px}
  /* The footer has room the header does not, and this is the only branding in
     that band, so the mark runs larger there. */
  .footer__brand img{height:140px}
}


/* ==========================================================================
   The Happiness Guarantee badge, in the footer.

   BugHerd #39, Randy: "We like the Happiness Guarantee logo in the footer as
   it is one of our differentiating factors. Is it possible to add it to this
   footer and link it to the page rather than just having it listed in the
   About section?"

   It sits under the social icons in the brand column and links to
   /happiness-guarantee/. The artwork is a navy disc, so it holds its own edge
   against the footer ground without needing a plate behind it — checked
   against that background, the navy and against white before shipping.

   The selector is more specific than .footer__brand img above, which sizes the
   white Jump! logo; without that the badge would inherit the logo's height.
   ========================================================================== */

.footer__guarantee{
  display:inline-block;
  margin-top:1.25rem;
  line-height:0;
  transition:transform .2s ease;
}
.footer__brand .footer__guarantee img{
  width:104px;
  height:auto;
  margin-bottom:0;
  display:block;
}
@media (min-width:1000px){
  .footer__brand .footer__guarantee img{width:124px}
}
@media (prefers-reduced-motion:no-preference){
  .footer__guarantee:hover{transform:translateY(-3px) rotate(-3deg)}
}


/* ==========================================================================
   The hero's bottom edge — confetti, then the sky band — on every page.

   Amy, September 23, 2026: "I feel like this treatment should also be on
   every page? it also looks weird and lowres on the other pages."

   THE CONFETTI. Both halves of "weird and lowres" are the same rule in
   jump.css being wrong about what the file is. dots-02.jpg is 1920x663, and
   roughly three quarters of it is blank white: the artwork is a single row of
   confetti and frogs along the BOTTOM edge. It is a border graphic. jump.css
   treats it as a texture — background-size:cover, background-position center
   TOP, opacity .45, requested at w=1400.

   Three consequences, all of them visible:

     - cover-cropping a 2.9:1 strip into a hero box that is nothing like that
       shape drops the confetti somewhere into the middle of the header
     - .45 over cream turns the pinks and limes into a grey-brown smudge
     - w=1400 stretched across a ~1990px hero is a 1.42x upscale of an
       already-downscaled file, at q=75, on artwork made of crisp cartoon
       outlines, which is the worst case there is for JPEG ringing

   band.css corrected all three for the homepage on September 21 and could not
   share the fix, because band.css is homepage-only and .hero is not. It left
   a note saying the other pages still had the old version and that nobody had
   yet said whether the confetti should follow them. Amy has now said it
   should, so the corrected rule lives here, where every page loads it, and
   has been deleted from band.css — one definition rather than a wrong one
   plus an override.

   ON RESOLUTION, honestly: w=1920 is the ceiling, because 1920 is the whole
   file. Above a 1920px viewport it upscales, and on a high-density display it
   is soft whatever we do — a 1986px hero at dpr 2 wants 3972 device pixels
   and the original holds 1920. jumpgymnastics.com serves the same file and
   has the same limit. A larger original is the only thing that lifts it.

   mix-blend-mode:multiply is NOT restated here; it is inherited from the
   jump.css rule and it matters. The source is coloured dots on a white
   ground, and over our cream hero normal blending would repaint the whole
   header white. Multiplied, the white field drops out and only the dots land,
   so full opacity leaves the confetti colours untouched on #FFF8EE.
   ========================================================================== */
.hero::before{
  background-image:url("/.netlify/images?url=https%3A%2F%2Fstorage.googleapis.com%2Fjump-gymnastics%2Fsite%2Fdots-02.jpg&w=1920&q=90");
  /* Bottom, not top — this is where the artwork actually is. */
  background-position:center bottom;
  /* Scale to the width and let the height follow, instead of cover-cropping
     a 2.9:1 strip to fill a box that is nothing like that shape. */
  background-size:100% auto;
  background-repeat:no-repeat;
  opacity:1;
}

/* THE SKY BAND. The solid middle of it: a flipped brush stroke sits on the
   hero's own bottom edge, this strip follows, then an upright brush. Two
   strokes facing each other is what gives the band a brushed edge on both
   sides — with one stroke the header goes cream, HARD LINE, sky, brushed
   edge, white, and a brush graphic with a ruler-straight edge reads as a
   mistake. Measured off jumpgymnastics.com on September 21.

   The markup is emitted by templates/heroedge.mjs. This rule was in band.css
   until September 23, when the treatment stopped being homepage-only; it has
   to be here because band.css is not loaded anywhere else. */
.brushband{
  display:block;
  width:100%;
  height:clamp(18px,2.2vw,32px);
  background:var(--sky);
  line-height:0;
}


/* ==========================================================================
   ACCENTS AS TEXT — the contrast fix

   A crawl of all twenty pages on September 24, 2026 measured 359 failing
   text elements on eighteen of them, and they all have one cause: the Jump!
   accents are mid-brightness colours. They are correct as FILLS, with dark or
   white text laid on them, and they fail as TEXT on white.

   Measured, worst first:

     .class__cta  "Register"          2.42:1  (needs 4.5)  <- the primary
                                                              conversion action
                                                              on the gym pages
     .cal__mon    month labels, 11px  2.24:1
     .classgroup__title, 32px         2.24:1  (needs 3)
     .class__program / .class__note   2.42:1
     .location__more / .program__more 2.57:1
     hero phone number, nine pages    3.39:1

   None of this is a new idea in this codebase. jump.css already carries
   --teal-ink with the note "white label needs the darker teal", and
   --pink-deep exists because plain pink failed white text in the footer. The
   pattern just never reached the places where an accent is the text.

   WHY color-mix AND NOT A TOKEN. Most of these rules read
   color:var(--c, ...), where --c is whichever accent that card was handed
   inline by the template. There is no fixed token that can be substituted,
   because the value is different on every card. Mixing the accent itself
   toward black darkens whatever it happens to be and keeps its hue.

   70% was chosen by measuring every accent that actually appears in these
   rules, not by eye:

     grass  #7AB742 -> 4.64:1   (the binding case)
     sky    #72A6D9 -> 4.93:1
     orange #F47920 -> 5.18:1
     teal   #00AAB0 -> 5.40:1
     pink   #F9428C -> 6.21:1

   Grass sets the number. Anything lighter than 70% and grass drops below the
   floor; anything darker and the colours start reading as brown.

   .class__cta[data-cta="later"] and [data-cta="waitlist"] are (0,2,0) and
   still win over the (0,1,0) selector below, which is correct — a class
   nobody can join already renders in slate and navy, and both pass.

   ON --pink-link, AND A MISTAKE WORTH KEEPING. The first pass gave the
   fixed-pink links --pink-deep, on the strength of its 4.68:1 against white.
   The hero phone number is not on white, it is on the cream hero, where
   --pink-deep measures 4.44:1 — a fail. That is precisely the trap this file
   calls out --teal-ink for a few blocks down, walked into from the other
   side. --pink-link is the same 70% mix as everything else here and clears
   every ground the site actually puts a link on: 6.21:1 on white, 5.89:1 on
   cream, 5.65:1 on the grey tint. One value, checked against all three,
   rather than three values each correct in one place.
   ========================================================================== */
:root{
  --pink-link:color-mix(in srgb,var(--pink) 70%,#000);
}

.program__more,
.location__more,
.class__program,
.class__cta,
.classgroup__title,
.cal__cta{
  color:color-mix(in srgb,var(--c,var(--pink)) 70%,#000);
}

/* Links that are always pink, wherever they sit. The hero phone number is the
   one that matters most — it is the call-us link, on nine pages. */
.hero__meta a,
.visit__address a,
.field--check a{color:var(--pink-link)}

/* White text ON an accent, rather than an accent as text. .level tone 1 was
   already correct — it uses --teal-ink, which is the precedent this whole
   block follows — and tone 3 is yellow carrying black. The rest were white on
   grass (2.42:1) or white on orange (2.76:1); mixed to 70% they carry white
   at 4.64:1 and 5.18:1.

   .classgroup__count and __note only appear once the Jackrabbit feed returns
   enough classes to group them, which is why they were missed on the first
   pass: they were not in the DOM when the rules were read. */
.level[data-tone="2"]{
  background:color-mix(in srgb,var(--grass) 70%,#000);
  border-color:color-mix(in srgb,var(--grass) 70%,#000);
}
.level[data-tone="4"]{
  background:color-mix(in srgb,var(--orange) 70%,#000);
  border-color:color-mix(in srgb,var(--orange) 70%,#000);
}
.classgroup__count,
.classgroup__note{
  background:color-mix(in srgb,var(--c,var(--navy)) 70%,#000);
}

/* The h1 accent is deliberately NOT mixed to 70%.

   Large text clears at 3:1, not 4.5:1, and this is the biggest type on the
   site — "confidence building" on the homepage is 50px. Orange on cream is
   2.62:1 there, so it fails, but only just. 84% takes it to 3.6:1, which is a
   change you have to look for; 70% would take it to 5.2:1 and would be a real
   alteration to the brand's loudest word to clear a bar it does not have to.

   (It was 88% until the fourth pass at the bottom of this file, which is
   where the four points went and why.)

   Both selectors are restated at their original specificity: .hero h1 .accent
   is (0,1,2) and .hero--location h1 .accent is (0,2,1), so each has to match
   its own or jump.css keeps winning on the location and program pages. */
.hero h1 .accent{color:color-mix(in srgb,var(--orange) 84%,#000)}
.hero--location h1 .accent{color:color-mix(in srgb,var(--c,var(--pink)) 84%,#000)}


/* ==========================================================================
   The footer ground — BugHerd #60

   Randy, September 23, 2026, pinned on the footer: "Can we switch this pink
   to our teal color? When we use too much pink, parents of boys think it is
   just for girls."

   NOT var(--teal). The footer is full-opacity white text on a solid ground,
   and #00AAB0 puts that at 2.84:1, which is a clear fail — teal is a much
   lighter colour than it looks next to pink.

   jump.css has already solved this exact problem once, in the other
   direction. Its comment above .footer says --pink-deep is "that pink
   darkened until white clears the bar at 4.68:1". So --teal-deep is that
   teal darkened until white clears the bar, and it measures 5.00:1 — a
   better margin than the pink it replaces, not a worse one.

   --teal-ink (#008489) was the first candidate, because it already exists in
   jump.css for precisely this job ("white label needs the darker teal" on
   .btn--teal). It was rejected on measurement: 4.504:1. That passes, but with
   no margin whatever, and the footer's link lists are set in --step--1. A
   colour that rounds to a fail in somebody else's contrast checker is not
   worth saving four hex digits over.

   The legal band is rgba(13,13,13,.32) over this, which composites to
   #04585C and carries white at 8.23:1, against 7.89:1 on the pink.

   ONE DECLARATION IS ALL THE FOOTER NEEDED. Everything else down there is
   #fff text, a rgba(255,255,255,.2) social chip or a --navy hover — none of
   it was pink-dependent, which is why this is a two-line override rather
   than a rewrite of the footer block in jump.css.

   --teal-deep is declared ON .footer rather than in :root, so a colour that
   exists for one element cannot be picked up by anything else. If it ever
   earns a second use, promote it to :root alongside --pink-deep and give it
   the same note.
   ========================================================================== */
.footer{
  --teal-deep:#007C81;
  background:var(--teal-deep);
}


/* ==========================================================================
   Photo placeholders say "Photo to come" and nothing else.

   Amy, September 23, 2026, on the step cards: "make all these images
   placeholders; don't name them."

   Every empty photo slot on this site renders a labelled box whose label is
   the shot's alt text — "A coach spotting a four-year-old on the low beam at
   Jump! South Austin". That was built deliberately, so that a missing
   photograph is visible rather than quietly filled with something that does
   not show what the block is about. It is also, read in a client review, a
   page covered in detailed instructions to the photographer.

   So the instruction is hidden, not deleted. The <span> is still in the
   markup, the alt text is still attached to the slot, and the build still
   prints every outstanding shot as a numbered list on every run — which is
   where a shot list belongs, because that one is complete and sorted and does
   not need somebody to scroll the site to read it. Nothing about the shoot got
   harder; the page just stopped talking to the photographer in front of the
   client.

   This lives in brand.css rather than pages.css because placeholders appear on
   every kind of page — home, program, prose and the four gyms — and brand.css
   is the only small stylesheet that layout.mjs loads on all of them. It comes
   after jump.css in that order, and .ph__label matches .ph__label at equal
   specificity, so the later rule wins without needing !important.

   To put the labels back, delete this block. That is the whole switch.
   ========================================================================== */
.ph__label{display:none}


/* ==========================================================================
   THE SAME FIX, FOR TEXT THAT IS NOT ON WHITE

   The 70% block above was calibrated against white, and for everything that
   sits on white it is correct. Re-crawling the site on September 24 with a
   fixed measuring script turned up a second group of failures that the first
   pass could not have caught, for two separate reasons.

   THE MEASURING BUG CAME FIRST, and it is worth writing down because it made
   the site look more fixed than it was. The crawler read computed background
   colours with a regex that only matched rgb()/rgba(). Every background on
   this site that is written as color-mix() computes to color(srgb r g b)
   instead, matched nothing, and fell through to a default of white — so each
   of these elements was measured against a white background it is not on.
   Two elements even reported 1.08:1, which is not a contrast ratio anything
   can produce by accident; that number was the tell. Parsing both formats is
   what exposed the real list.

   THE ACTUAL PROBLEM is that these four elements are coloured text sitting on
   a pale tint OF THE SAME ACCENT — a chip is var(--c) text on a 15% wash of
   var(--c) — or on the grey --bg-alt. Tinting the ground toward white raises
   its luminance, and the ratio falls. 70% clears 4.5:1 against white and does
   not clear it against a wash of its own hue:

     .chip       "129 classes running"   4.20 pink  3.69 teal
                                         3.62 orange  3.29 grass
     .class__note "Members only"         4.22 grass
     .cal__mon    "Oct"                  4.30 grass
     .ph__tag     "Photo to come"        2.87 lime  3.74 sky
                                         3.89 orange  4.25 rose

   Every one is a near miss rather than the invisible text the first pass
   dealt with, and near misses are exactly what gets waved through.

   THE NUMBERS BELOW ARE SOLVED, NOT PICKED. Each is the largest mix that
   clears 4.5:1 for every accent the rule can actually receive, computed
   against that rule's own background rather than against white, then given a
   little margin. The accent sets differ, which is why the percentages do:

     .chip  takes --c from the location cards, which content/locations/*.json
            colours teal, pink, orange, grass or sky. 63% -> worst 4.55
            (grass). Solving this against --yellow as well would have forced
            48.5% and turned every chip on the site muddy to protect a colour
            that never lands on one; the single "yellow" in content is on an
            awards stat tile, not a chip.

     .ph    cycles PH_TONES = sky, lime, orange, rose, teal, grass. 53% ->
            worst 4.63 (lime). Lime binds every placeholder rule, because
            #BACD32 is the lightest accent in the kit — it does not clear
            4.5:1 as text against plain white until 61%.

     .class__note and .cal__mon keep taking --c from the location accents, so
     58% -> worst 5.67 and 5.78 (grass). They are pulled out of the 70% group
     above rather than overridden here, so there is one declaration per
     element and not a rule plus a correction.

   .ph__tag ALSO LOSES ITS opacity:.75, and this one is not a tuning choice.
   Opacity composites the text back toward whatever is behind it, so on a
   light ground it caps the darkness the colour can reach: with .75 in place
   there is NO mix percentage, including 0% — pure black — that clears 4.5:1
   for lime. The rule was unsatisfiable, not badly tuned. The tag keeps its
   visual recession from being small, uppercase and letterspaced, which costs
   no contrast.

   WHY THE ORIGINALS WERE NOT CONTRAST-DERIVED. jump.css sets .chip at 78%
   and .ph at 72%; both predate any measurement and were chosen to look
   right. That is the whole story — there is no constraint being broken here,
   only one that was never applied.
   ========================================================================== */
.chip{color:color-mix(in srgb,var(--c,var(--sky)) 63%,var(--black))}

.ph{color:color-mix(in srgb,var(--ph,var(--sky)) 53%,var(--black))}
.ph__tag{opacity:1}

.class__note,
.cal__mon{color:color-mix(in srgb,var(--c,var(--pink)) 58%,#000)}


/* ==========================================================================
   THIRD PASS — WHITE ON AN ACCENT, AND TWO STRAGGLERS

   Re-crawling all twenty pages after the fix above cleared every element the
   previous pass had flagged and surfaced five it had not. Four were
   genuinely unreachable earlier: .classgroup__more and .class__state are
   rendered by the Jackrabbit feed and are not in the DOM until it returns,
   and .skip is parked at left:-9999px until it takes focus. The fifth was
   not hidden at all.

   .btn IS THE ONE WORTH BEING EMBARRASSED ABOUT. "Find a Location" sits in
   the header of all twenty pages, it is the site's primary navigation CTA,
   and it was white on raw --pink at 3.39:1 the whole time. The block above
   has a section headed "White text ON an accent, rather than an accent as
   text" — it listed the elements measured failing that day (.level tones,
   .classgroup__count) and never asked what else on the site is white on an
   accent. The buttons are the obvious answer.

   THE FIX WAS ALREADY WRITTEN DOWN, TOO. jump.css sets
   .btn--teal{background:var(--teal-ink)} and comments it "white label needs
   the darker teal". That is this exact problem, solved, for one variant out
   of five. The default pink ground and --rose never got the same treatment:

     --pink   #F9428C  white 3.39:1   <- .btn default, and .skip
     --rose   #E366A6  white 3.13:1   <- .btn--rose, worse, and absent from
                                         the crawl only because no crawled
                                         page currently renders one
     --navy   #3551A3  white 7.35:1   fine
     --teal-ink        white 4.50:1   passes, with nothing to spare

   --pink-deep already exists in jump.css as "that pink darkened until white
   clears the bar at 4.68:1", which is exactly what a pink button ground
   needs to be. It was the footer's colour until BugHerd #60 turned the
   footer teal, so it is currently declared and unused — this gives it back
   its original job rather than adding a sixth pink to the palette.

   --rose-deep is new, and is --rose darkened the same way until white clears
   at 4.62:1. It is declared on the variant that uses it, not in :root, on
   the same reasoning as --teal-deep in the footer block above.

   ALL BUTTONS MOVE, NOT ONLY THE SMALL ONES. .btn--sm is what the crawler
   caught, because at 17px it needs 4.5:1 while a full-size .btn can reach
   24px and only needs 3:1, which 3.39 scrapes. Recolouring just the small
   ones would put two different pinks on the same page and read as a bug.
   One pink, passing at every size, is both the correct fix and the one that
   looks deliberate.

   THE VARIANTS ARE RESTATED BELOW, AND THEY HAVE TO BE. .btn--rose,
   .btn--teal, .btn--navy and .btn--ghost are all single-class selectors in
   jump.css, exactly as specific as .btn is here. brand.css loads afterwards,
   so without restating them a rule meant to change the default pink would
   win against every variant and repaint the whole site's buttons — turning
   the ghost button's ground solid pink, which is the sort of thing that
   ships. Their values are unchanged except for the ghost, which is dealt
   with in its own block at the foot of this file; they are here only to sit
   later in the cascade than the line above them.
   ========================================================================== */
.btn{background:var(--pink-deep)}
.skip{background:var(--pink-deep)}

/* Unchanged values, restated to outrank the line above. See the note. */
.btn--rose{--rose-deep:#B75285;background:var(--rose-deep)}
.btn--teal{background:var(--teal-ink)}
.btn--navy{background:var(--navy)}
.btn--ghost{background:var(--navy)}

.classgroup__more{color:color-mix(in srgb,var(--c,var(--sky)) 70%,#000)}

.class__state--open{color:#487324}


/* ==========================================================================
   FOURTH PASS — WHAT THE CRAWLER HAD BEEN SKIPPING

   Two failures the first three passes never saw, and the reason they did not
   is worth recording, because it means every "clean" result before this one
   was clean about less of the page than it claimed.

   Almost everything on this site animates in on scroll: elements carry
   data-anim, start at opacity 0 and get .is-in when they reach the viewport.
   The crawler multiplies an element's colour by its composited opacity and
   skips anything under 0.05, which is correct — text at opacity 0 has no
   contrast to measure. But it means an element that had not animated yet was
   silently dropped rather than measured. The pass that found these two was
   the first where the observer had fired for these particular sections, and
   they turned up immediately. Neither is a regression; both have been there
   the whole time, behind an opacity of 0.

   .eyebrow IS --teal-ink AGAIN, for the third time in this file. It is 4.50:1
   on white — the "passes, but with no margin whatever" this file warns about
   two blocks above, in the note explaining why the footer did not use it. Off
   white it simply fails:

     white     4.50      cream     4.27
     paper     4.17      limepale  4.04

   and .eyebrow sits on every one of those, because it labels section intros
   and those sections are tinted as often as not. 70% is the same mix as the
   rest of this file and clears all four with room: 5.35 white, 5.07 cream,
   4.95 paper, 4.80 limepale.

   The lesson --teal-ink keeps teaching is that a value checked against one
   background is not a value, it is a coincidence. It is now checked against
   every ground the site actually has.

   THE H1 ACCENT GOES 88% -> 84%, which is the same mistake in miniature and
   mine. The note above says 88% was chosen by measuring orange on cream at
   3.4:1 against a 3:1 bar for large text. Orange is the homepage's accent.
   The location and prose heroes take --c from the page, and the employment
   page is grass, which at 88% on cream is 2.93:1 — under the bar, on a 50px
   heading. I picked a percentage from one colour and applied it to six.

   84% is the largest mix where every accent clears 3:1 on cream, checked
   rather than assumed: grass 3.20, sky 3.38, orange 3.62, teal 3.73, pink
   4.40, navy 8.67. Both selectors move, including the homepage's fixed
   orange, so the same word is the same colour on every page — a four point
   difference nobody would see as a design change but would eventually see as
   an inconsistency.
   ========================================================================== */
.eyebrow{color:color-mix(in srgb,var(--teal) 70%,#000)}


/* ==========================================================================
   FIFTH PASS — THE REST OF WHAT THE ANIMATION WAS HIDING

   Forcing every [data-anim] element to its end state before measuring, rather
   than hoping the scroll observer had fired, turned up four more. Three are
   here; all three are the two patterns this file has already fixed twice, in
   places the earlier crawls could not reach.

     .field em        12x  3.39:1  raw --pink on white
     .filter__reset    4x  3.39:1  raw --pink on white
     .pricefilter__pill.is-on  4x  3.39:1  white on raw --pink

   .field em is the one that matters. It is the required-field asterisk on the
   inquiry form — the lead form, on all four gym pages, the thing this whole
   site exists to get filled in — and "required" was being marked in a colour
   that fails against the label beside it.

   The filter controls were unreachable for a different reason than the feed
   elements in the pass above: .filters and .pricefilter both ship with the
   HTML `hidden` attribute and are un-hidden by location.js and pricing.js
   once they have something to filter. A crawler that reads the page without
   running its scripts never sees them at all.

   .pricefilter__pill.is-on IS NOT FIXED HERE, and that is a load-order fact
   rather than an oversight. It is defined in pricing.css, which layout.mjs
   emits AFTER brand.css (checked on the deployed page: jump.css, brand.css,
   pricing.css, team.css). At equal specificity the later sheet wins, so a
   .pricefilter__pill.is-on rule written here would simply lose. It is fixed
   in pricing.css, where it lives.

   NOT FIXED ANYWHERE, AND DELIBERATELY: .quote__stars, the ★★★★★ on every
   review card, is --yellow on white at 1.33:1 — the lowest ratio on the
   site. It is left alone because darkening it is a brand decision and not a
   bug fix: gold stars on white is what a rating looks like everywhere, and a
   yellow that passed 4.5:1 would not read as gold. The rating is also
   already available as text to a screen reader via the aria-label on the
   row. Flagged for Amy rather than changed on my own judgement.
   ========================================================================== */
.field em{color:var(--pink-link)}
.filter__reset{color:var(--pink-link)}


/* ==========================================================================
   THE GHOST BUTTON GETS A FILL

   Amy, September 28, 2026: "buttons on some of these subpages need the learn
   more to have a fill."

   The secondary button was a navy label inside a 3px navy keyline on a
   transparent ground. Beside the solid pink primary it reads as the weaker
   of the two by some margin, which is the point of a secondary button right
   up until the page's actual next step is the one wearing the outline —
   "Learn More" under the About paragraph, "Explore Our Gyms" in the program
   heroes, "Get Directions" on the gym pages.

   THE FILLED APPEARANCE IS NOT NEW, and that is what settles the only real
   question here. jump.css already ships exactly this combination as
   .btn--ghost:hover — navy ground, white label — so every background on this
   site that carries a ghost button has been displaying the filled version on
   hover since launch. There is no ground where this is a surprise, and no
   need to audit the eleven places the class is used. It becomes the resting
   state rather than the hover state.

   White on --navy is 7.35:1, the same figure the third-pass block above
   cites when it lists navy as the one button ground that never needed
   darkening.

   THE THREE STATE RULES ARE NOT OPTIONAL. jump.css sets
   .btn--ghost:hover{box-shadow:none} to drop the inset keyline on hover, and
   at (0,2,0) that outranks .btn:hover, .btn:active and .btn:focus-visible —
   all of which are also (0,2,0) and all of which sit EARLIER in the cascade.
   So without restating them here the filled button would have no lift on
   hover, no press on click and, worst of the three, no focus ring. The
   values are copied from .btn's own, not invented.

   The ground itself is set further up, in the restated-variants group, so
   that it stays next to the other four .btn-- lines rather than being the
   one variant whose colour is somewhere else in the file.
   ========================================================================== */
.btn--ghost{
  color:#fff;
  box-shadow:0 4px 0 rgba(0,0,0,.12);
}
.btn--ghost:hover{box-shadow:0 7px 0 rgba(0,0,0,.14)}
.btn--ghost:active{box-shadow:0 2px 0 rgba(0,0,0,.14)}
.btn--ghost:focus-visible{box-shadow:var(--ring), 0 4px 0 rgba(0,0,0,.12)}

/* Wide screens have the room for the full-size mark beside the menu. */
@media (min-width:1200px){ .header__logo img{height:76px} }

/* On a phone the confetti strip scaled to a 375px width came out about
   130px tall, so the frogs were the size of the dots (Randy, BugHerd #122,
   10/16/2026: "Is it possible to get these frog heads and dots to show a
   little better down here?"). Below 700px it draws at twice the width and
   crops the sides instead, which doubles every frog and dot. */
@media (max-width:699px){
  .hero::before{background-size:200% auto}
}

/* The three pieces of the hero edge (brush, sky strip, brush) are separate
   elements, and at phone sizes the browser rounds each to a different
   sub-pixel, which left a hairline of cream between them (Randy, BugHerd
   #127: "This transition is separated from the one below it"). Overlap each
   seam by a pixel so there is nothing to round into. */
.hero > .divide:last-child{position:relative;z-index:1;margin-bottom:-1px}
.brushband{position:relative;margin-top:-1px;margin-bottom:-1px;padding-bottom:1px}
.brushband + .divide{position:relative;z-index:1}


/* ==========================================================================
   Heading colors - BugHerd #169, approved by Randy 10/27/2026 ("I approve
   that font color changes. They can be changed throughout.")

   His rule, from the original site: green headings on white, white headings
   and text inside the colored sections, grey for sub-items such as card
   titles. No black lettering. The page heroes are handled separately (#212).

   "body" leads each selector only for weight: path.css, reasons.css and
   waves.css load after this file on some pages and restate band text at
   (0,2,1), so these need to outrank them without !important.
   ========================================================================== */
:root{--heading:var(--grass);--subhead:var(--ink)}
body main h2{color:var(--grass)}
body main h3,body main h4{color:var(--subhead)}

/* Colored bands: white headings, eyebrows and copy. Cards that sit on a band
   are white themselves, so everything inside them keeps the white-page rule. */
body .section--lime :is(h2,h3,.eyebrow,.lede,p,li,figcaption),
body .adventure :is(h2,h3,.eyebrow,p){color:#fff}
body .section--lime :is(p,li,.lede){text-shadow:0 1px 2px rgba(18,0,82,.28)}
body .section--lime :is(.pillar,.program,.quote,.location,.class,.yearcal__item,.party-book,.party-gym,.fees,.pricing,.award,.faq,form) :is(h2,h3,h4,p,li,.eyebrow,.lede,figcaption){color:var(--text);text-shadow:none}
body .section--lime :is(.pillar,.program,.quote,.location,.class,.yearcal__item,.party-book,.party-gym) :is(h2,h3,h4){color:var(--subhead)}
body .section--lime :is(.pillar,.program,.location,.class,.yearcal__item,.party-book,.party-gym) p{color:var(--text-soft)}
body .section--lime .quote :is(blockquote,p){color:var(--ink)}
body .section--lime .quote figcaption{color:var(--navy)}
body .section--lime .reasons__card :is(h3,p,li){color:#fff}
/* Written pages: their h2 and h3 were navy (pages.css). Same rule as above. */
body .prose h2{color:var(--grass)}
body .prose h3{color:var(--subhead)}

/* ==========================================================================
   Page heroes - BugHerd #212, Randy picked option A on 10/28/2026: "I prefer A
   ... Natalie agrees that we would prefer to leave it the blue and orange."

   White ground instead of cream, so the colors read true (Jillian: the orange
   "is not our orange"). The homepage accent goes back to the real brand
   orange; it was darkened to 84% only to clear 3:1 on cream.
   ========================================================================== */
.hero{background:#fff}
.hero h1 .accent{color:var(--orange)}

/* Gymnastics class cards in their level color (BugHerd #269). The stripe
   uses the true level color; the small words use a darker shade set per
   level in location.js, so Yellow and Silver stay readable on white. */
.class[data-level-color] .class__program,
.class[data-level-color] .class__cta{color:var(--ct)}
.class[data-level-color] .class__cta[data-cta="full"],
.class[data-level-color] .class__cta[data-cta="later"]{color:var(--slate)}
.class[data-level-color] .class__cta[data-cta="waitlist"]{color:var(--navy)}
