/* home.css — the KODA homepage
 * ===========================================================================
 * ONE PAGE, ONE JOB: get a West Michigan homeowner from "my carpet is gross"
 * to a booked visit, on a phone, in under two minutes.
 *
 * Research Jeremy brought (2026-09-09): the vast majority of visitors never
 * scroll past the first screen. So the page is built top-heavy — the booking
 * card, the review seal and the newest reviews are all above or immediately
 * below the fold, and everything below is reinforcement for the minority who
 * keep going.
 *
 * ═══ THIS FILE DEFINES NO COLOUR ═══
 *
 * koda-theme.css ("Nocturne", approved 2026-09-09) is the single source of
 * truth for the colour of every pre-login page, and it says so in its own
 * header: change colour there, not in a page. Every token below is an ALIAS
 * onto a Nocturne variable. Nothing here introduces a hex.
 *
 * That rule was learned the hard way. The first cut of this redesign rebuilt
 * the homepage from CLAUDE.md §3's burgundy-and-cream palette and never read
 * koda-theme.css — which loads last, wins every tie, and had RETIRED those warm
 * neutrals by name (#fbf8f3 among them) the same day. Jeremy: "You changed the
 * entire color scheme to exactly what we are trying to get away from … go back
 * to blue and gold, keep the same feel." CLAUDE.md §3 has not caught up with
 * Nocturne yet; when the two disagree about a public page, NOCTURNE WINS.
 *
 * The system, restated so it is obvious what may be painted where:
 *   NAVY   chrome and anchor bands — the bar, the dark sections, the footer.
 *   WHITE  + ONE mist grey (#eff3f7) carry the content bands between them.
 *   BURGUNDY is the ACTION colour. It is what a customer clicks, and it is
 *          nothing else. Not a heading, not a rule, not a background.
 *   GILT   is the accent of last resort: stars, the seal, one hairline, the
 *          top edge of the booking card. Never a large area, and never small
 *          text on a light ground (it measures 2.37:1 on white).
 *
 * ═══ THE FEEL: LETTERPRESS ═══
 *
 * What this page adds to Nocturne is DEPTH — Jeremy's note on the old page was
 * that it "does feel luxurious but also feels flat". Nocturne sets the colour;
 * this file gives its surfaces weight:
 *
 *   PAPER  a faint grain over the page, so a white card is a raised sheet
 *          rather than a void.
 *   PRESS  raised things carry a light top edge and a shadow beneath; pressed
 *          things invert both. --lift-1..4 and --press.
 *   RULE   hairlines are Nocturne's --koda-rule, never a grey of our own.
 *
 * ═══ TYPE ═══
 * Nanum Myeongjo (700/800) display, DM Sans body — CLAUDE.md §4, which the
 * colour change does not touch. Sizes respect §4.1's web minimums with room to
 * spare: body 17px, secondary 15px, footer 14px, nothing below 13px. KODA's
 * audience skews 35+. test/home-font-sizes.test.js enforces the floor.
 * =========================================================================== */

/* THE TOKENS LIVE IN koda-chrome.css (2026-09-14) — both blocks, the :root
   primitives and the .koda-v2 aliases onto Nocturne. Every pre-login page
   needs them to paint the bar, so they went with it. Nothing below changed:
   koda-chrome.css is linked first on this page. */

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

/* `hidden` must actually hide. The UA sheet sets `display:none`, which any
   `display:flex`/`grid` rule in this file silently outranks — that is how the
   booking card's step rail carried on showing four ticks under "Request
   received". Every hide on this page goes through the attribute, so it is
   enforced once, here. */
[hidden]{ display: none !important; }

html{ scroll-behavior: smooth; scroll-padding-top: calc(var(--bar-h) + 14px); }
@media (prefers-reduced-motion: reduce) {
  html{ scroll-behavior: auto; }
  *, *::before, *::after{ animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body{
  font-family: var(--font-body);
  font-size: 17px;              /* §4.1 body floor is 16 — this sits above it */
  line-height: 1.65;
  color: var(--text);
  background: var(--page);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
/* The grain lies over the whole page, under everything interactive. */
body::before{
  content: '';
  position: fixed; inset: 0;
  background-image: var(--grain);
  opacity: .035;
  pointer-events: none;
  z-index: 9999;
  mix-blend-mode: multiply;
}

body.koda-page-home img{ max-width: 100%; display: block; }
body.koda-page-home a{ color: var(--ink); text-decoration: none; }
body.koda-page-home button, body.koda-page-home input, body.koda-page-home select, body.koda-page-home textarea{ font: inherit; color: inherit; }

body.koda-page-home .wrap{ width: min(1240px, 100% - 40px); margin-inline: auto; }
@media (max-width: 640px) { body.koda-page-home .wrap{ width: min(1240px, 100% - 28px); } }

body.koda-page-home .skip{
  position: absolute; left: -9999px; top: 0; z-index: 10000;
  background: var(--ink); color: #fff; padding: 14px 22px; font-size: 16px; font-weight: 600;
}
body.koda-page-home .skip:focus{ left: 12px; top: 12px; }

body.koda-page-home :focus-visible{ outline: 3px solid var(--steel); outline-offset: 3px; border-radius: 4px; }

/* ── Display type ───────────────────────────────────────────────────────── */
/* `text-wrap: balance` — NO ORPHANS IN A HEADLINE.
   Jeremy, 2026-09-10, about the services heading breaking after "We": "Looks
   weird ending that line like that. Make sure other section headers don't have
   weird cutoffs like that."
   A browser breaks a heading at the last word that fits, which is how "…Stand
   On It, We" and "Our Trucks Run Here Every / Week" happen. `balance` asks it
   to even the lines out instead, which is exactly the rule a typesetter would
   apply and it costs one declaration. It is a hint: a browser without it lays
   the heading out the way it always did, so nothing regresses. Where there is
   ONE right break rather than a balanced one, the markup still carries an
   explicit <br> (the services h2, the Why KODA h2) — balance would only
   usually find those. */
h1, body.koda-page-home h2, body.koda-page-home h3{ font-family: var(--font-display); font-weight: 700; line-height: 1.14; color: var(--ink); text-wrap: balance; }
body.koda-page-home h1{ font-size: clamp(40px, 5.4vw, 66px); letter-spacing: -.015em; }
body.koda-page-home h2{ font-size: clamp(30px, 3.4vw, 42px); letter-spacing: -.01em; }
body.koda-page-home h3{ font-size: clamp(20px, 1.5vw, 23px); }

body.koda-page-home .eyebrow{
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-body); font-size: 14px; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--steel-deep);
  margin-bottom: 14px;
}
body.koda-page-home .eyebrow::before{
  content: ''; width: 26px; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, var(--steel), transparent);
}
body.koda-page-home .lede{ font-size: 19px; color: var(--muted); line-height: 1.6; max-width: 62ch; }

/* ═══ THE BAR AND THE DRAWER LIVE IN koda-chrome.css NOW (2026-09-14) ════════

   Jeremy: *"The pre-login pages per-service (and likely per area, etc. Any
   pre-login page) should all have the bar the same up top. Please fix that.
   Same with the whole footer section."*

   Nine hundred lines of bar, review pennant, call lockup, mobile sheet,
   sign-in drawer, form fields and buttons moved out of this file, together
   with both token blocks and the footer. They are on twenty-one pages now.

   THE SELECTORS CHANGED SCOPE, NOT SHAPE: `body.koda-page-home X` became
   `body.koda-chrome X`, and <body> here carries BOTH classes. koda-chrome.css
   is linked BEFORE this file, so where the two ever collide at equal
   specificity the home page still wins — which is the right way round.
   ═══════════════════════════════════════════════════════════════════════════ */
/* ═══════════════════════════════════════════════════════════════════════════
   HERO
   Rebuilt 2026-09-09 (Jeremy): "Show a bright clean inviting living room and
   display the intake form in a clean, intentional, and user-friendly manner."

   THE TWO HALVES ARE ONE PROBLEM. The card was 1,030px on its counting step —
   taller than the laptop it sat on — so the photograph was never in the frame
   with it, and no amount of work on the picture would have shown. Every number
   below is set against a hero that fits a 900px screen: the card's steps are
   built to about 480px (see THE BOOKING CARD), and the copy column is tuned to
   sit beside that rather than under it.

   THE SCRIM IS A VEIL, NOT A WASH. The old hero drowned the photograph under a
   burgundy-to-slate overlay; Jeremy, twice: "the blue overlay is way too dark",
   then "the real image is bright and clean and with the blue overlay it feels
   dark. Don't change the color scheme, just simply figure out how to make the
   text pop with the photo light rather than darkly overlaid." So the polarity
   is inverted — the veil is WHITE, weighted over the left where the copy sits
   and gone by two thirds across, and the type is NAVY. The room stays a bright
   room and the words are darker than anything behind them.
   ═══════════════════════════════════════════════════════════════════════════ */
/* THE TOP PADDING CLEARS THE FLAG. The pennant hangs 78px below the bar, and on
   a phone the bar collapses to a logo, a call button and a burger, which puts
   the flag over the middle of the hero copy. The clearance is the overhang plus
   a real gap: get it wrong and the first line a visitor reads is behind a gold
   triangle. */
/* ═══ THE COLUMN SITS HIGHER ON THE FRAME (2026-09-16) ═════════════════════
   Jeremy: *"Can we lift this up higher so the text doesn't get lost in the
   background. We like the picture but the stool blends the text."*

   THE PHOTOGRAPH CANNOT MOVE AND THAT IS THE WHOLE CONSTRAINT. The band is
   1266 x 1013 and the file is 1900 x 1069, so `cover` scales to HEIGHT — the
   crop fills the band exactly top to bottom and there is no vertical play at
   all. `background-position`'s second value is inert here; panning it is a
   no-op, which is why nobody should try it again. The only thing that can move
   is the type.

   So the type moves. Four small reductions — 36px off the hero's top padding,
   10 off the masthead's rule, 6 off the mark and 4 under the headline — lift
   the whole lockup 56px, which is enough to take the sub-line off the console
   table and its glass candlesticks (the "stool") and set it on the fireplace
   surround instead. Nothing shrinks: the mark is still the 3x it was asked to
   be, the headline is still 66px, the spec strip is untouched. The column just
   starts earlier.

   THE FOUR NUMBERS MOVE TOGETHER. Take the padding back on its own and the
   lockup lands where it started; tighten the internal gaps on their own and the
   headline barely shifts. They are commented as one change in four places
   because they are one change. */
body.koda-page-home .hero{ position: relative; padding: calc(var(--bar-h) + 52px) 0 52px; overflow: hidden; }
/* PHOTOGRAPHY, CLAUDE.md §9.2: "object position is content". The subject of
   this frame is the FLOOR — a pale rug running the width of a sunlit room — so
   the crop is pinned low and never left at centre, which would show ceiling. */
/* ═══ THE PHOTOGRAPH IS A BAND, NOT A BACKDROP (2026-09-10) ═════════════════
   Jeremy: "Do we have a way to not expand the image between steps 1 and 2."

   It was `inset: 0` — the full height of .hero, whatever that turned out to be.
   And .hero is as tall as the booking card, which is 711px on the counting step
   and 1,999px on the details step for a four-area house. `background-size:
   cover` over a box that nearly triples in height does not letterbox, it
   RE-CROPS: the room zooms in and the frame slides, so tapping Continue looked
   like the picture had lurched. Every step of the wizard was quietly restaging
   the photograph behind it.

   So the photograph gets a height of its own and keeps it. `min(100%, …)` means
   it still fills a short hero exactly as it did — nothing changes on step one —
   and simply stops at the cap once the card grows past it, with the veil
   resolving into the page colour so the band dissolves rather than ending at a
   line. That is the treatment the stacked layout has used below 1000px since
   the redesign; it is now what every width does, and the crop is fixed for the
   whole of the form. */
body.koda-page-home .hero{ --hero-band: 1040px; }
body.koda-page-home .hero-photo{
  position: absolute; inset: 0 0 auto; z-index: 0;
  height: min(100%, var(--hero-band));
  background-size: cover; background-position: center 68%;
}
/* MEASURED, NOT GUESSED. The wall behind the headline reads about #EDE9E3. At
   .58 white that lifts to roughly #F6F4F1, and navy #16202e on it measures
   about 14:1. Where the veil crosses furniture (around #C8BEB2) the same weight
   gives #E4DFD8 and about 11.5:1. Both are several times what AA asks, so the
   veil can stay thin and the room can stay bright. .hero-copy carries a white
   halo underneath for insurance, not as the contrast mechanism. */
body.koda-page-home .hero-scrim{
  position: absolute; inset: 0 0 auto; z-index: 1;
  height: min(100%, var(--hero-band));
  background:
    /* ── A COLUMN, NOT A CIRCLE (2026-09-11) ────────────────────────────────
       Jeremy: *"The text still seems to get lost on the page. Can you help me
       find a way to position this so text is clear yet we still see the photo
       the same?"*

       THE OLD LIFT WAS A CIRCLE AND THE COPY IS A RECTANGLE. A radial centred
       at 17% 46% is brightest on the headline and falls off in every direction
       from there — so the sub-headline below it and the spec strip below THAT
       sat further down the ramp, on progressively less ground, which is exactly
       the order in which they were getting lost. The headline was never the
       problem; it is 66px with 5px strokes. The 19px line under it was.

       PANNING THE PHOTO COULD NOT FIX IT EITHER, and it is worth writing down
       why so nobody tries again. The band is 1425 × 1014 and the file is
       1900 × 1069, so `cover` scales to HEIGHT and leaves about 377px of
       horizontal play — real, but not enough to move the fireplace, the TV and
       the black shelf uprights (this room's whole left third, and the busiest
       passage in it) out from behind a column that is 44% of the screen wide.

       SO THE LIFT IS A COLUMN NOW: even from top to bottom, so every line of
       type gets the same ground, and gone by 70% — which is the half of the
       room a visitor is being invited to imagine clean, and is pixel for pixel
       what it was before. "We still see the photo the same" is literally true
       of everything right of the copy, and the picture is not dimmed anywhere:
       this only ever adds white under the words so the words can stay dark.

       96deg rather than 90 so the edge falls with the copy block rather than
       cutting straight down through it. */
    linear-gradient(96deg,
      rgba(255,255,255,.84)  0%,  rgba(255,255,255,.78) 28%,
      rgba(255,255,255,.50) 44%,  rgba(255,255,255,.16) 58%,
      rgba(255,255,255,0)   70%),
    /* THE LANDING. Where the band ends the page has to already be the page, or
       the photograph stops at a hard horizontal line halfway down a form. Only
       the last tenth is spent on it, so a hero that fits inside the band (step
       one, every phone) is untouched. */
    linear-gradient(180deg, rgba(255,255,255,0) 88%, var(--page) 100%),
    /* the barest overall lift, so the band reads as one surface */
    linear-gradient(0deg, rgba(255,255,255,.04), rgba(255,255,255,.04)),
    /* and a whisper of navy at the very top so the bar has something to sit on */
    linear-gradient(180deg, rgba(22,32,46,.17) 0%, rgba(22,32,46,0) 13%);
}
/* The weave stays the full height of the hero — it is the page's own texture,
   not part of the photograph, and it is what keeps the paper below the band
   from reading as a blank margin. */
body.koda-page-home .hero-weave{ position: absolute; inset: 0; z-index: 1; background-image: var(--weave); opacity: .45; }
body.koda-page-home .hero .wrap{ position: relative; z-index: 2; }

/* ═══ THE SMUDGE, AND WHERE IT CAME FROM (2026-09-10) ═══════════════════════
   Jeremy: "there is a weird smokey background on the text … That smoke behind
   the text appears throughout this intake form, we need to remove that
   everywhere."

   koda-theme.css §5 styles hero copy for the eighteen pages where a hero IS a
   photograph with words on it:

       .koda-v2 .hero p { color: #fff; text-shadow: 0 1px 12px rgba(0,0,0,.45); }

   On this page the booking card lives INSIDE .hero — it is the hero — so that
   rule reached every <p> in the wizard: every step hint, every question label,
   every "Tap any that apply" was carrying a 12px black blur on white paper. At
   14px that is not a shadow, it is a smear, and any <p> the redesign had not
   given a colour of its own was white on white.

   ONE RULE, AND ITS PLACEMENT IS LOAD-BEARING. It matches the theme's
   specificity exactly (0,2,1) and home.css loads second, so it wins the tie —
   and it sits ABOVE every .step-hint / .blk-hint / .acards-left rule in this
   file, which are the same specificity again and therefore still win over it.
   Move it below them and the form goes uniformly dark-on-white and the gold
   "still to answer" counter goes with it. The three places that genuinely want
   a halo (the h1 and the sub, over the photograph) take it back by inheriting
   .hero-copy's WHITE one, which is the shadow this hero was designed around.

   NOTE THE MISSING `body`. The selector below is `.koda-page-home .hero p`, not
   `body.koda-page-home .hero p`, and the difference is the whole mechanism:
   `body` is a TYPE selector, so adding it would make this (0,2,2) — one notch
   ABOVE every `body.koda-page-home .blk-hint` rule further down, which would
   then lose their own colours to this one and take the whole form uniformly
   dark, gold counter and all. Without it this is (0,2,1): exactly the theme's
   weight, winning only by coming second in the cascade, and losing in turn to
   anything below that says what colour it actually wants. */
.koda-page-home .hero p{ color: var(--text); text-shadow: none; }
/* The card is the widest it can be and still leave the copy a real column:
   the tile grid inside it wants three across at a comfortable size, and the
   calendar wants five. Below 1000px they stack. */
body.koda-page-home .hero-grid{
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 530px);
  gap: 48px; align-items: start;
}
/* THE CARD'S WIDTH IS A FUNCTION OF THE STEP, and the rules that decide it
   live with the intake grid further down this file (see THE CARD TAKES THE
   WIDTH THE STEP NEEDS). `.wide` — the old "give the card 110px back from step
   2 on" — is gone: step 2 is a seven-column grid now and 640px was never going
   to be enough for it. */

/* ═══ "SAME KODA" — THE RETURNING-CUSTOMER NOTE (2026-09-16) ════════
   Jeremy: *"We need a small 'Same KODA, New Website' type of message at the top
   of the hero so returning customers know we are the same company."*

   IT SITS IN AIR THAT WAS ALREADY THERE. A line dropped in above the masthead
   pushes the mark, the headline, the sub-line and the spec strip down by its
   full height — and this column was lifted 56px earlier today for the express
   purpose of getting the sub-line off the console table and its candlesticks
   (see THE COLUMN SITS HIGHER ON THE FRAME). Paying for the pill out of the
   hero's top padding instead is not an option either: the booking card shares
   that padding, and it would climb to within a few pixels of the bar.

   SO THE PILL IS LIFTED INTO THE MARK'S OWN DEAD BAND. koda-logo.png.png is a
   1200 × 700 canvas with its artwork at y 192 → 485, which at the rendered 534px
   width leaves 85px of transparent nothing ABOVE the ink — the same wasted band
   that --mark-air subtracts BELOW it, never yet reclaimed. The pill's box is
   33px (15px type + 8px padding each side + a 1px edge) and its gap is 13px, so
   `.masthead` takes exactly those 46px back and every object below it starts
   where it started: the mark, the headline and the card beside them do not move
   by a pixel.

   --notice-h IS THE ONE NUMBER AND IT IS TWO PLACES. Change the pill's padding,
   its type size or its margin and this token has to follow, or the whole column
   shifts down by the difference.

   THE BAND IS BIG ENOUGH AT EVERY WIDTH THE MARK HAS: 85px at 534, 75px at 472,
   64px at 400 — all comfortably clear of 46.

   LEGIBILITY IS THE FILL, NOT A RING. The sub-line further down wears a
   hairline outline because it is bare type on a photograph; a 13px tracked cap
   needs a ground, for the same reason the spec strip at the foot of this column
   needs one (see THE PLATE UNDER THE SPEC STRIP): a halo saves a letter from
   standing on a shadow, it does not make a row of small caps read as ONE
   object.

   ═══ BUT THE GROUND IS A PLATE, NOT A PILL (2026-09-17) ══════════════════════
   Jeremy: *"give the pill a redesign to better fit with the rest of the page's
   layout. It feels very flat/boring."*

   HE IS RIGHT, AND THIS PAGE ALREADY WROTE DOWN WHY. The note on the wax seal
   further down this file diagnoses exactly this failure for a different object:
   a small rounded box "has the same silhouette as a button, a badge, a pill, a
   highlighted span — every other small coloured box a page can contain — so the
   eye files it with them and reads it fourth." That is what a 999px lozenge with
   a gold hairline was. It did not look like a caption on the mark; it looked
   like a browser notification that had got loose, and it was the one object in
   the hero with a hard outline in a composition whose every other element — the
   gilt rule, the spec plate, the scrim, the mark itself — has soft edges or
   none.

   SO THE BOX IS GONE AND THE OBJECT IS TYPE AND RULES. Five children on one
   line: a gilt rule, "Same KODA" in the display face, a struck gold lozenge,
   "NEW LOOK" in tracked caps, a gilt rule. The two rules are `.masthead::after`
   — the same gilt gradient, fading out at the ends, at a smaller scale — so the
   mark is now bracketed: a ruled caption above it, a rule below it, one lockup.
   Before this it was a badge floating over the top of one.

   THE TWO HALVES ARE SET DIFFERENTLY ON PURPOSE. "Same KODA" is the half that
   answers the question, so it is the display face, reading as the mark's own
   name repeated. "NEW LOOK" is the half that is news, so it is the spec strip's
   voice — tracked caps, body face — which is the same pairing that strip uses one
   tier down the column ("6–12 Hours" over "DRY TIME"). Two faces, one system,
   and neither of them is a button label.

   THE GROUND IS THE SPEC STRIP'S, FEATHERED ON ALL FOUR SIDES. Same recipe as
   `.hero-facts::before`: .86 white, a horizontal ramp that is spent entirely
   OUTSIDE the words, a mask that softens top and foot so the rules sit ON the
   plate rather than on its edge, and two pixels of backdrop blur so the room
   behind goes quiet without going away. Four soft sides, no box.

   --notice-h IS STILL 46px AND THAT IS DELIBERATE. 15px of type + 9px of
   padding each side = 33, plus the 13px gap = 46, which is what the old pill
   measured to the pixel. Every number downstream of it — the masthead's negative
   margin, the 92px of top padding that clears the review pennant below 1240 (see
   BELOW 1240 THE PENNANT IS IN THE PILL'S SEAT), the 11px of clearance over the
   mark's first visible pixel at the 400px mark size — is unchanged, so nothing
   else in this hero moved for the redesign. */
body.koda-page-home .hero-copy{ --notice-h: 46px; }
body.koda-page-home .hero-notice{
  position: relative;
  /* The rule length is a token because it is the one measurement that has to
     shrink on a phone — see the 640 override at the foot of this block. */
  --notice-rule: 44px;
  --notice-feather: 46px;
  display: flex; align-items: center; justify-content: center;
  gap: 11px;
  width: max-content; max-width: 100%;
  margin: 0 auto 13px; padding: 9px 0;
  line-height: 1;
  /* Declared, not inherited: `.koda-page-home .hero p` above sets every <p> in
     this section to --text with no shadow, and this one wants the column's own
     ink. Same specificity, later in the file. */
  color: var(--hero-ink); text-shadow: none;
}
/* THE PLATE. `z-index: -1` puts it at the bottom of `.hero .wrap`'s stacking
   context — behind the type and the rules, still well above the scrim at 1 and
   the photograph at 0. The horizontal inset is the feather, so all of the ramp
   is spent in the empty margin either side and every glyph stands on the full
   .86 white. */
body.koda-page-home .hero-notice::before{
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  inset: -6px calc(-1 * var(--notice-feather));
  border-radius: 2px;
  background: linear-gradient(90deg,
    rgba(255,255,255,0)   0,
    rgba(255,255,255,.86) var(--notice-feather),
    rgba(255,255,255,.86) calc(100% - var(--notice-feather)),
    rgba(255,255,255,0)   100%);
  -webkit-backdrop-filter: blur(2px) saturate(1.04);
          backdrop-filter: blur(2px) saturate(1.04);
  -webkit-mask-image: linear-gradient(180deg,
    rgba(0,0,0,0) 0, #000 13px, #000 calc(100% - 13px), rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(180deg,
    rgba(0,0,0,0) 0, #000 13px, #000 calc(100% - 13px), rgba(0,0,0,0) 100%);
}
/* THE RULES. Each fades toward the OUTSIDE, so the object gives out into the
   plate's own feather instead of stopping at two hard ticks. `flex: none` or
   they are shrunk before the type is, which on a 375px screen would leave two
   gold specks. */
body.koda-page-home .hero-notice .notice-rule{
  flex: none; width: var(--notice-rule); height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, rgba(201,164,76,0), var(--gold));
}
body.koda-page-home .hero-notice .notice-rule:last-child{
  background: linear-gradient(90deg, var(--gold), rgba(201,164,76,0));
}
/* THE LOZENGE. A 7px square turned through 45°, struck off the same metal as
   the review pennant and the wax seal — lit at the top-left corner, deepest at
   the foot — so the one ornament in the line belongs to the page's gold rather
   than being a bullet character that happens to be gold. It is `aria-hidden`,
   and the flex `gap` is what separates the two halves for a reader that skips
   it. */
body.koda-page-home .hero-notice .notice-gem{
  flex: none; width: 7px; height: 7px; transform: rotate(45deg);
  background: linear-gradient(135deg, var(--gold-lit), var(--gold) 55%, var(--gold-deep));
  box-shadow: 0 1px 2px rgba(22,32,46,.22);
}
body.koda-page-home .hero-notice strong{
  font-family: var(--font-display); font-weight: 700;
  font-size: 15px; letter-spacing: 0; white-space: nowrap;
}
/* 13px with .15em of tracking, which is the spec strip's label a half-pixel
   larger — and it is FULL ink, not a tinted navy, for the reason that strip
   gives at length: matching a tone is not worth the contrast on a surface that
   is a photograph. */
body.koda-page-home .hero-notice span{
  font-family: var(--font-body); font-weight: 700;
  font-size: 13px; letter-spacing: .15em; text-transform: uppercase;
  white-space: nowrap;
}
/* A PHONE HAS 335px OF COLUMN AND THE LINE WANTS MOST OF IT. The type cannot
   give — 13px is at CLAUDE.md §4.1's floor for an audience that skews 35+ — so
   the rules and the gaps do, which is the right order: they are the two things
   in the line carrying no information. */
@media (max-width: 640px){
  body.koda-page-home .hero-notice{
    --notice-rule: 20px; --notice-feather: 26px; gap: 8px;
  }
}

/* ═══ THE MASTHEAD ═════════════════════════════════════════════════════════
   Mark, gilt rule, headline — one lockup.

   ENLARGED AND CENTRED, 2026-09-10. Jeremy: "Increase our logo by 3x and center
   it between 'A' and 'Feel' so it's centered over the hero text." It was a
   104px mark tucked into the top-left corner of the column — placed, not
   designed — and the copy column had 200px of empty air beside it that the
   booking card next door did not.

   ═══ WHAT "CENTRED BETWEEN 'A' AND 'FEEL'" MEANS IN CSS ═══

   Over the HEADLINE's own measure, not the column's. The column is 662px wide
   at 1440 and the headline's longest line is 360px of it, so centring the mark
   on the column would have parked it well to the right of the words it is meant
   to sit over — technically centred, visibly adrift. (That longest line was
   "A home you feel", 452px, until the 09-16 re-break; it is "feel proud of" now.
   See --mark-w below for the one number that had to follow it.)

   So the lockup shrink-wraps the headline (`width: max-content`, which for an
   <h1> carrying an explicit <br> is the longer of its two lines) and the mark
   is centred inside that. The rule under it follows the same centre. The mark
   is then as large as that measure allows, up to 3× — which is 3× on every
   screen where the headline has room for it and a shade under on the widest
   desktop, where the h1 stops growing at 66px but the column keeps going. A
   logo that overhung the column to reach a round multiple would sit in the
   gutter and clip the moment the window narrowed.

   THE CAP IS 3× THE OLD MARK: 104px tall × 1.714 (1200 × 700 native) = 178px
   wide, so 534px. Height follows from width, so the aspect can never be
   stretched — CLAUDE.md §2. */
body.koda-page-home .hero-lockup{ width: max-content; max-width: 100%; margin-inline: auto; }
/* THE NEGATIVE TOP IS THE "SAME KODA" PILL BEING PAID FOR OUT OF THE MARK'S
   TRANSPARENT CANVAS — see THE RETURNING-CUSTOMER NOTE above; it is exactly
   the height the pill occupies, so nothing below the masthead moves.
   Bottom is 12px, not 22 — see THE COLUMN SITS HIGHER ON THE FRAME above. */
body.koda-page-home .masthead{ margin-top: calc(-1 * var(--notice-h)); margin-bottom: 12px; }
/* `width: 0; min-width: 100%` IS THE WHOLE TRICK, AND IT IS NOT A HACK FOR ITS
   OWN SAKE. The lockup shrink-wraps its content, and a plain `width: 100%` on a
   1200px-wide PNG contributes its INTRINSIC 1200px to that calculation — so the
   lockup came out as wide as the column and the mark centred 105px right of the
   headline, which is the exact failure this was meant to fix. A zero width
   contributes nothing to the intrinsic pass; the min-width then fills the
   measure the headline set, in the layout pass, where percentages are real.
   Height is never stated, so the aspect cannot be stretched — CLAUDE.md §2. */
body.koda-page-home .hero-logo{
  /* ══ THE MARK'S BOX IS NOT THE MARK (2026-09-16) ══════════════════════════
     Jeremy: *"It looks like it and the carpet/upholstery text section could be
     moved up to better center vertically between the logo and the 6-12 hours
     dry time."*

     HE IS DESCRIBING 215 TRANSPARENT PIXELS. koda-logo.png.png is a 1200 × 700
     canvas and the artwork inside it runs y 192 → 485: there is 27.4% of
     nothing above the mark and 30.7% of nothing below it. Rendered at 534px the
     box is 311px tall and only 130px of that is ink, so the gilt rule under the
     masthead was not sitting 10px below the logo at all — it was sitting 106px
     below the last pixel anybody can see, and 31px above the headline. A rule
     that is three times closer to the headline than to the mark stops reading
     as a divider between them and starts reading as an underline on the
     headline, which is why the whole h1 + sub-line group looked like it had
     slid to the bottom of the space it was in.

     SO THE DEAD BAND IS SUBTRACTED, IN CSS, FROM THE FILE'S OWN NUMBERS. The
     band below the mark is 215/700 = 30.71% of the rendered HEIGHT, and height
     is width / 1.7143, so it is 17.92% of the rendered WIDTH — whatever that
     width turns out to be. `--mark-air` is then what is actually wanted between
     the mark's last visible pixel and the rule, and the margin is the one minus
     the other. The result goes negative (about -56px at 534) and it is supposed
     to: it is pulling back space the PNG is not using.

     ONE PERCENTAGE BASIS, WHICH IS WHY THIS IS SAFE. `--mark-w` carries a
     `100%` term, and a percentage in `margin-bottom` resolves against the
     containing block's WIDTH — the same basis `min-width` uses. Both properties
     are on this element, so they cannot disagree. Do not move `--mark-w` up to
     .hero-copy to share it with the sub-line: over there `100%` would mean the
     662px column rather than the 452px lockup, and the two would silently drift
     apart.

     THE FILE IS UNTOUCHED — CLAUDE.md §2. Nothing is cropped, redrawn or
     re-exported; the real logo is still the src and its aspect is still the
     browser's own. Only the empty margin around it stops taking up room. */
  /* ══ 1.256 IS THE HEADLINE THE MARK WAS SIZED AGAINST (2026-09-16) ══════
     `100%` here is the lockup, and the lockup is `max-content` of the h1 — its
     LONGEST line. Re-breaking the headline after "you" to centre it (see the
     note in index.html) took that longest line from "A home you feel" (451.8px
     at the 66px ceiling) to "feel proud of" (359.7px), and the mark, which is
     sized off exactly that measure, would have quietly come out 20% smaller on
     every screen under 1280. Jeremy asked for a 3× mark; a centring fix is not
     allowed to take it away again.

     So the measure is scaled back to the one it used to be: 451.8 / 359.7 =
     1.2559. Both lines are set in the same face at the same size, so the ratio
     holds at every breakpoint rather than only at the ceiling. (The em's 6px of
     padding is the one px-based term in it, 1.7% of the line, so the ratio
     drifts under a percent across the whole clamp — far inside the rounding
     the browser is doing anyway.)

     CHANGE THE HEADLINE'S WORDING AND THIS NUMBER IS WRONG. It is the only
     thing in the stylesheet that knows what the h1 says. */
  --mark-w: min(534px, calc(100% * 1.2559 + max(0px, 100vw - 1280px)));
  --mark-air: 40px;
  width: 0;
  /* THE CAP, AND THE ONE PLACE THE MARK IS ALLOWED PAST THE TEXT. 534px is 3×
     the mark it replaced (104px tall × 1.714 native aspect = 178px wide). The
     corrected measure is 452px at the h1's 66px ceiling, so on a wide desktop
     the last 82px of that 3× has to come from somewhere: it comes from the
     gutter, symmetrically, and only from gutter that actually exists. Below a
     1280px window the second term is zero and the mark simply sits on the
     corrected measure. It stays centred on the headline either way — that is
     what the customer sees, and it is what was asked for. Measured after the
     09-16 re-break: 277px at 360, 335 at 900, 407 at 1100, 452 at 1280, 534 at
     1440 — the same five numbers it produced before, which is the point. */
  min-width: var(--mark-w);
  height: auto; display: block;
  /* `margin: 0 auto` centres a box INSIDE its container and gives up the moment
     the box is wider than it, resolving both margins to zero and hanging the
     whole overhang off the right — which is how a mark meant to be centred on
     the headline ended up starting at its first letter and ending past the card.
     The
     transform has no such opinion: it centres on the measure whether the mark
     fits inside it or bleeds either side of it. */
  position: relative; left: 50%; transform: translateX(-50%);
  /* 17.92% of the width is transparent canvas below the mark — see THE MARK'S
     BOX IS NOT THE MARK above. What is left is `--mark-air` of real space. */
  margin: 0 0 calc(var(--mark-air) - var(--mark-w) * .1792);
  /* Two shadows: a tight one that separates the mark from whatever the
     photograph is doing behind it, and a wide soft one that gives it weight.
     A white glow would have done neither — it is already on a light ground. */
  filter: drop-shadow(0 1px 1px rgba(16,24,36,.18))
          drop-shadow(0 8px 20px rgba(16,24,36,.16));
}
body.koda-page-home .masthead::after{
  content: ''; display: block; width: 132px; height: 3px; margin: 0 auto;
  background: linear-gradient(90deg, rgba(201,164,76,0) 0%, var(--gold) 22%, var(--gold) 78%, rgba(201,164,76,0) 100%);
}
/* ═══ `margin-inline: auto` IS NOT TIDINESS, IT IS THE CENTRING (2026-09-16) ══
   Jeremy, twice, and the second time was not a matter of taste: *"We still
   aren't centered with 'A home you feel proud of'. Everything aligns other than
   the 'a home you feel proud of'."*

   IT IS ONLY WRONG ON A WIDE SCREEN, WHICH IS WHY IT SURVIVED A MEASURED PASS.
   `width: max-content` gives the h1 a box the width of its longest line — 360px
   — and a fixed-width block inside a WIDER block sits flush LEFT unless it is
   told otherwise. Normally it is not wider: the lockup is `max-content` too, so
   it comes out as the h1 and the two edges coincide.

   THE MARK IS WHAT MAKES THE LOCKUP WIDER. `.hero-logo` is `width: 0` precisely
   so it contributes nothing to that intrinsic pass (see the note below it), but
   `min-width` contributes as well, and --mark-w carries a `100vw - 1280px`
   term. A percentage is dropped from an intrinsic calculation; an absolute
   length is not. So past about 1640px the mark's min-width starts winning the
   masthead's max-content, the lockup swells toward 534 — and the h1, still
   360px and still flush left, slides (534 - 360) / 2 = 87px off the axis while
   the mark, the gilt rule, the sub-line and the spec strip all stay on it.

   Measured at 1920 before this line: column, lockup, mark, sub and strip all
   centred on 664; the headline on 577. At 1440 the vw term is only 160px, the
   lockup is the h1's own 360, and everything measures perfect — which is
   exactly how a pass that only looked at 1280 and 1440 signed this off twice.

   One declaration removes the whole class of fault: the box is centred in
   whatever the lockup turns out to be, so it can never matter again what the
   mark does to the lockup's width. */
body.koda-page-home .hero-lockup h1{
  width: max-content; max-width: 100%; margin-inline: auto;
}
/* The two smaller marks only restate the WIDTH and the AIR; the dead-band
   subtraction is the base rule's and follows them down on its own. */
@media (max-width: 900px){
  body.koda-page-home .hero-logo{ --mark-w: min(472px, calc(100% * 1.2559)); --mark-air: 34px; }
  body.koda-page-home .masthead{ margin-bottom: 20px; }
}
@media (max-width: 640px){
  body.koda-page-home .hero-logo{ --mark-w: min(400px, calc(100% * 1.2559)); --mark-air: 28px; }
  body.koda-page-home .masthead::after{ width: 104px; }
}

/* ── THE HALO IS AN OUTLINE NOW (2026-09-10) ────────────────────────────────
   Jeremy: "We need to add a bit of shading behind the hero text. Not a lot,
   don't overlay the whole image, just find a way to have the text not blend to
   the background without distracting from the photo or the text. Maybe a very
   fine outline in white just to make the text stand out. Looks like we have
   that going just a hair but for example, the word 'every' gets lost on the
   background."

   HE NAMED THE EXACT FAILURE. "Every Surface" is the last fact in the spec
   strip, which is the lowest thing in the copy column — so it sits where the
   veil has thinned to almost nothing AND where the photograph has its busiest
   passage, the sofa and the window frame behind it. Two soft glows (12px and
   3px) put light NEAR a glyph; what a glyph needs against a busy ground is
   light ON ITS EDGE, and a 3px blur spread over 360° is not that.

   So the halo is two things stacked, and the order matters:

     1. AN OUTLINE. Eight 1px offsets at 0/45/90/…°, each with a 1px blur. That
        is a hairline of white tight against every stroke — the "very fine
        outline in white" verbatim. It costs nothing over the photograph
        because it is only ever one pixel wide.
     2. A BLOOM, kept. The wide 14px glow still lifts the local ground, which is
        what keeps the outline from reading as a sticker cut out of the room.

   WHY NOT -webkit-text-stroke. A stroke is drawn CENTRED on the glyph outline,
   so half of it eats into the letterform: 1px of stroke on a 12.5px cap thins
   the type visibly and turns the tracking to mush. A text-shadow ring is drawn
   entirely OUTSIDE the fill, so the letterforms are exactly the letterforms.

   AND NOT MORE VEIL. "Don't overlay the whole image" is a real constraint and
   the right one — the hero's whole design is a bright room the type sits on
   (see THE SCRIM IS A VEIL above). Nothing here touches .hero-scrim. */
/* FULL INK, NOT --text. Jeremy, 2026-09-10: "Darken all of the text so it
   stands out on the white background." --text (#1e2530) is the body colour for
   a white PAGE; this column sits on a photograph of a sunlit room, where the
   brightest patches under the veil run to about #F7F5F2 and every glyph is
   competing with a window. --ink (#16202e) is the darkest thing in the palette
   and it is what the headline was already using; the sub-headline and the spec
   strip now use it too, so the column reads as one voice instead of three. */
/* ═══ AND THEN THE RING CAME OFF (2026-09-14) ══════════════════════════════
   Jeremy: *"Try removing the white outline on the hero font and use black font
   other than 'Proud'."*

   BOTH HALVES OF THAT ARE ONE MOVE. The ring existed to keep --ink (#16202e, a
   navy-black) legible where the veil thins; take the ring away and the type has
   to carry itself, and a navy-black on a sunlit room is the wrong ink to ask
   that of. Pure brand black — CLAUDE.md §3's dark-text token — is roughly a
   third of a stop darker on the same ground, and it is the difference between
   the column needing an outline and not.

   WHERE THE LEGIBILITY ACTUALLY COMES FROM NOW. The veil (.hero-scrim) is
   unchanged and still runs 84% → 78% white down the whole copy column, and the
   spec strip at the foot — the one place the veil has thinned to almost
   nothing — has had its own white plate under it since earlier today. So the
   two surfaces that were doing the work are both still there; the ring was the
   third layer, and it was the one that showed.

   `text-shadow: none` is declared, not omitted. Leave it off and koda-theme.css
   applies `.koda-v2 .hero h1 { text-shadow: 0 3px 30px rgba(0,0,0,.55) }` — a
   30px black blur meant for white type on a dark photograph, which under black
   type reads as a smudge.

   --hero-ink is one token for the whole column so the headline, the sub-line
   and the spec strip cannot drift apart again. "proud" is the deliberate
   exception and sets its own colour below. */
body.koda-page-home .hero-copy{
  --hero-ink: #1a1a1a;
  color: var(--hero-ink);
  text-shadow: none;
  /* ═══ ONE AXIS FOR THE WHOLE COLUMN (2026-09-15) ═══
     Jeremy: *"Please center the hero logo and text so they all align vertically
     and the text is all centered."*

     Every piece of this column was already centred on something, and no two of
     them on the same thing: the mark and the gilt rule on the headline's
     measure, the headline's second line on its first, the sub-line and the spec
     strip on nothing at all — both were flush left against a column edge the
     mark had drifted away from. Four objects, three axes, and the mark visibly
     off over the words below it.

     `text-align: center` here is the one declaration that settles all of it,
     because every child inherits it; `margin-inline: auto` on the three boxes
     below then puts the BOXES on the same axis as the type inside them. The
     lockup already shrink-wraps the headline and the spec strip already
     shrink-wraps its three facts (`width: max-content` on both), so auto
     margins centre the words rather than a box with slack in it — which is the
     failure the strip's own note further down describes. */
  text-align: center;
}
/* The theme's `.koda-v2 .hero h1` blur has to be turned off by name here — see
   the note on .hero-copy above. */
body.koda-page-home .hero-copy h1{ color: var(--hero-ink); text-shadow: none; }
/* BOTH LINES CENTRE IN ONE BOX (2026-09-11, revised 2026-09-16). Jeremy:
   *"Center the words 'proud of' in the hero between 'a home you feel' so it
   isn't left justified."* `.hero-lockup h1 { width: max-content }` makes the box
   exactly the LONGER of the two lines, and `text-align: center` then places the
   shorter one inside it; the mark and the gilt rule above are centred on the
   same box, so the whole lockup shares one axis.

   WHICH LINE IS LONGER CHANGED ON 09-16. It was line one, "A home you feel";
   the re-break that fixed the optical centring (see index.html) made it line
   two, "feel proud of", at 360px against line one's 341. The mechanism did not
   change — max-content is still the longer line — but anything that quoted 452px
   as "the measure" did, including --mark-w above. */
/* 14px, not 18 — see THE COLUMN SITS HIGHER ON THE FRAME above. */
body.koda-page-home .hero h1{ margin-bottom: 14px; text-align: center; }
/* THE WEAKEST WORD ON THE PAGE WAS THE EMPHASISED ONE. "proud" was set in
   --steel-deep, Nocturne's slate for small text on mist — and it was the one
   word in a 66px headline that was not full ink, sitting over the brightest
   part of the room, under a gilt highlighter. --steel-ink is the same brand
   slate taken down until it clears 7:1 on that surface, so the emphasis still
   reads as a different colour and no longer reads as a faded one. */
body.koda-page-home .hero h1 em{
  font-style: italic; color: var(--steel-ink); position: relative;
  /* A hand-drawn underline rather than a text-decoration: it reads as emphasis
     someone made rather than a link the browser drew. */
  background: linear-gradient(90deg, rgba(201,164,76,.55), rgba(201,164,76,.55)) 0 88% / 100% 9px no-repeat;
  border-radius: 2px; padding: 0 3px;
}
/* ── THE SUB-LINE GETS A RING BACK, AND ONLY A RING (2026-09-16) ────────
   Jeremy: *"We need to add the very thin white outline to the 'Carpet,
   upholstery and every…' part of the hero, through 'serving all of West
   Michigan'. When mobile it just gets lost in the background."* And then, on
   what it must not be: *"Don't make it glow/fade, just make it very very fine
   just to separate the letters from the background."*

   IT IS ONE LINE OF TYPE, ON A PHOTOGRAPH, AT NINETEEN PIXELS, and stacked it
   lands square on the sofa and the striped ottoman — the busiest passage in
   the room, at the depth where the veil has already thinned so the furniture
   can show. Weight alone does not settle that; 600 makes the strokes thicker
   and the strokes are still the same colour family as what is behind them.

   THIS IS NOT THE HALO COMING BACK. The two rings this line wore in a week
   (09-11, 09-14) were each an outline STACKED ON A BLOOM — a wide soft glow
   that lit the ground around the words, which is the thing Jeremy has now
   explicitly ruled out, and which is what made 19px copy the loudest object in
   the column. There is no bloom here and there is no blur: eight one-pixel
   offsets at 0/45/90/…°, every one of them at ZERO blur radius, which draws a
   hairline of white hard against each stroke and stops. It cannot fade, it
   cannot halo, and it is exactly one pixel wide however big the screen is.

   WHY NOT -webkit-text-stroke. A stroke is centred on the glyph outline, so
   half of it eats the letterform: a white 1px stroke on 19px type thins the
   type and mushes the tracking. A text-shadow ring is drawn entirely OUTSIDE
   the fill, so the letters are exactly the letters.

   THE HEADLINE AND THE SPEC STRIP ARE DELIBERATELY NOT GIVEN THIS. The h1 is
   66px with five-pixel strokes and has never been the line that disappeared,
   and the strip's labels are small caps with real tracking. Jeremy named one
   line; one line gets it.

   THE EXTRA STEP OF WEIGHT STAYS. 600 rather than 400 is what a 19px line
   needs on a photograph, and it costs the picture nothing. */
body.koda-page-home .hero-sub{
  font-size: 19px; line-height: 1.55; color: var(--hero-ink); font-weight: 600;
  text-shadow:
     1px  0    0 #fff, -1px  0    0 #fff,
     0    1px  0 #fff,  0   -1px  0 #fff,
     1px  1px  0 #fff, -1px  1px  0 #fff,
     1px -1px  0 #fff, -1px -1px  0 #fff;
  max-width: 34ch; margin: 0 auto 22px;
  /* Centred, this line broke "…all of West / Michigan" and left one word
     stranded on a third row. `balance` is the browser evening the rows out for
     us; it applies to short blocks only, which this is, and every h2 on the
     page is already set the same way. */
  text-wrap: balance;
}

/* ═══ THE EXPERIENCE SEAL ══════════════════════════════════════════
   Jeremy, 2026-09-10: *"this is more what I was looking for with the 44 years
   stamp that you have on the hero section and the why koda section. Use the
   stamp attached and put it to the right of the lead form, not in the hero
   text."*

   ═══ WHAT THE REFERENCE ACTUALLY CHANGED ═══

   The plate this replaces was a gold RECTANGLE with debossed type in it, and
   the trouble with a gold rectangle is that it is a label. It has the same
   silhouette as a button, a badge, a pill, a highlighted span — every other
   small coloured box a page can contain — so the eye files it with them and
   reads it fourth. A seal has a silhouette nothing else on a web page has, and
   that is most of what makes it read as a mark of standing rather than as a
   piece of interface. It is also, literally, the difference between something
   printed on a document and something pressed into one.

   ═══ HOW THE WAX IS BUILT ═══

   The manufacture is the review pennant's, so every gold object on this page
   comes off one metal — but the geometry is new, and it is all in the SVG:

     THE SCALLOPS  fifteen 26-unit arcs struck around a 104-unit circle. Regular
                   rather than hand-irregular: at 176px an irregular blob reads
                   as a badly drawn circle, and the lobes are what carry the
                   silhouette.
     THE FOIL      a six-stop ramp — bright at the crown, through the mid-tone,
                   a specular band where a curved face throws light, then the
                   deepest tone at the foot.
     THE POOL      a radial fill inside the rim, lit from the upper left and a
                   shade deeper than the wax around it. That difference is what
                   makes the rim a raised LIP rather than a drawn circle.
     THE FLAKE     a diagonal guilloche at 6% over the whole face.
     THE DEBOSS    every large glyph drawn twice, a light echo one unit low and
                   the near-black face over it.

   ═══ AND WHY IT SITS ON THE CARD ═══

   "To the right of the lead form" — the booking card IS the right-hand column,
   so `.book-col` becomes the positioning context and the seal is pressed onto
   its top-right corner, overlapping the header and the first of the form the
   way a wax seal overlaps the paper it was dropped on.

   ═══ IT IS INSIDE THE CARD ON BOTH AXES, AND THAT IS THE FIX OF 2026-09-11 ══

   The seal used to hang 26px above the card and 22px past its right edge, and
   it was being beheaded on one axis and guillotined on the other:

     THE TOP.   The overhang put the crown in the exact band the fixed bar
       occupies. Scroll fifty pixels and the brass rail took the top off the
       medallion. A fixed header covers everything that passes under it — a
       paragraph survives that, a circle does not.
     THE RIGHT. `.hero` is `overflow: hidden` (it has to be; the photograph and
       the wash are full-bleed inside it) and it clips at the DOCUMENT edge,
       which is the page gutter — 20px — outside .wrap. The seal's outermost
       scallop reaches 17px past .wrap and its 26px shadow another 26 beyond
       that, so on every viewport narrower than 1280px, where .wrap is
       `100% - 40px` rather than a centred 1240, the right-hand scallops were
       being sliced flat. The old note here claimed two pixels of clearance; it
       was measuring the 176px BOX and the artwork is not the box.

   So the geometry is now measured from the painting, not the element. The
   scallop tips reach 83px from the centre (113 viewBox units of 240, scaled to
   176px) and the 176px box puts the centre 88px in from its own right edge, so
   `right: 6px` lands the outermost tip 11px INSIDE the card and the whole
   shadow inside the gutter. `top: -8px` puts the crown (11.7px of transparent
   margin at the top of the box) four pixels BELOW the card's top edge.

   Nothing of the seal is ever the outermost or topmost thing on the page any
   more: the card's own edges meet the bar and the gutter first, and the seal
   goes under the rail with the paper it is pressed onto. It reads as a stamp
   on the corner of a form, which is what it is, and it cannot be cut at any
   width by anything.

   IT STANDS UPRIGHT (2026-09-17). It used to sit six degrees off the vertical,
   on the theory that a hand-pressed seal is never quite square. Jeremy read that
   as a mistake rather than as a gesture — *"the 44 years of excellence stamp is
   slightly off-center, tilted left a bit"* — and he is right about what it costs:
   the ring is a perfect circle, so the tilt is legible ONLY in the type, and a
   line of type a few degrees off true reads as a layout bug, not as wax. The
   artwork is symmetric about x=120 to the unit, so level is centred. Every copy
   of this seal is now 0 degrees; keep it that way.

   ═══ TYPE SIZES ARE VIEWBOX UNITS AND THAT IS A TRAP ═══

   The SVG is 240 units wide and renders at 176px, so every font-size below is
   multiplied by 0.733 before anybody reads it. CLAUDE.md §4.1 puts the web
   floor at 12px, which makes 16.4 units the real floor here — "OF", the
   smallest thing on the seal, is set at 18 and lands at 13.2px. Shrinking the
   seal shrinks the type with it, which is why the small-screen width is 168px
   and not 140px: at 140px "OF" would render at 10.5px and the seal would be
   breaking the type floor without a single font-size in this file changing. */
body.koda-page-home .book-col{ position: relative; }
/* z-index 6, NOT 3. The card's own foil edge (.book::before) is also z-3, and
   it belongs to an element that comes LATER in the markup than the seal — so at
   equal z-index the rail won a paint-order tie and drew a gold line straight
   across the top of the medal (Jeremy, 2026-09-11: "the top line going through
   the top of the medal"). The seal is pressed ONTO the card, so it outranks
   everything the card draws at its own edge. Safe at any height because the
   stamp is `pointer-events: none` and the card's own pop-ups (the address
   suggestions) open low in the body, nowhere near this corner. */
body.koda-page-home .seal-stamp{
  position: absolute; z-index: 6; top: -8px; right: 6px;
  width: 176px; margin: 0;
  pointer-events: none;          /* it is a mark, not a control */
}
body.koda-page-home .seal-stamp-svg{
  display: block; width: 100%; height: auto;
  /* Cast onto the card and onto the room behind it. `drop-shadow` follows the
     scalloped alpha, which is the whole reason the seal reads as an object
     lying on the page; a box-shadow would draw a square behind a circle. */
  filter:
    drop-shadow(0 2px 3px rgba(16,24,36,.28))
    drop-shadow(0 14px 26px rgba(16,24,36,.38));
}
/* GILT IS NOT A TEXT COLOUR AND THIS DOES NOT MAKE IT ONE. Nocturne measures
   the gilt at 2.37:1 and bars it for small type on a light ground. Nothing here
   breaks that: the letters are near-black (--gold-ink, 8.1:1 on the foil) and
   the gold is the PLATE they are pressed into. */
body.koda-page-home .seal-stamp-ink{ fill: var(--gold-ink); }
/* The light echo, drawn first and one unit LOW. Never above: a highlight above
   a glyph reads as embossed, which is a different tool. */
body.koda-page-home .seal-lo{ fill: rgba(255,255,255,.5); }
body.koda-page-home .seal-rule{ stroke: #2c2109; stroke-opacity: .5; stroke-width: 2; }
body.koda-page-home .seal-n{
  font-family: var(--font-display); font-weight: 800; font-size: 78px; letter-spacing: -.02em;
}
body.koda-page-home .seal-y{
  font-family: var(--font-display); font-weight: 800; font-size: 30px; letter-spacing: .1em;
}
body.koda-page-home .seal-of{
  font-family: var(--font-body); font-weight: 700; font-size: 18px; letter-spacing: .16em;
}
/* 20, NOT 22, AND THE WHOLE BLOCK MOVED UP WITH IT.
   "EXPERIENCE" is the longest word on the seal and it sits lowest, where the
   ring has closed in hardest — and the measurement that matters is at the
   BASELINE, not at the cap. At 22/.085em on a 196 baseline it was 150 units
   wide against an inner chord of 121: the feet of both E's were outside the
   rim, which is the one flaw that makes a seal look like clip art. So the text
   block lifted (baselines 196 -> 187, and everything above it in proportion)
   and the size came down: 133 units against a chord of 140, clear by three and
   a half a side. Still 14px at the smallest size this seal ships (168px), over
   CLAUDE.md §4.1's 12px floor. */
body.koda-page-home .seal-t{
  font-family: var(--font-body); font-weight: 700; font-size: 20px; letter-spacing: .07em;
}

/* BELOW 1000px THERE IS NO "RIGHT OF THE FORM" TO BE TO — the hero grid has
   stacked and the card is the full width of the page. So the seal stops being
   attached to a corner and becomes a centred medallion ABOVE the card, which is
   the one thing it must not go back to being: a line inside the hero text. */
@media (max-width: 1000px){
  body.koda-page-home .seal-stamp{
    position: static; width: 168px; margin: 0 auto 18px;
  }
}

/* ON THE NIGHT BAND IT STANDS BESIDE THE HEADLINE, NOT UNDER IT.
   It used to be centred below the sub-line at 208px with 22px above it, which
   is 230px of column spent before the reader reached the five tiles that prove
   the claim the headline makes. `.why-head` sets the head and the seal on one
   row — the text keeps its centred axis inside its own box, the medallion sits
   to its right, and the tiles come up by the whole height of the seal.

   The shadow goes deeper than the booking card's: on navy a soft grey shadow is
   invisible and the seal floats. */
/* THE HEADLINE KEEPS THE PAGE'S CENTRE LINE. A flex row would have centred the
   PAIR, which slides the headline sixty pixels left of the tiles underneath it
   and reads as a typo in the layout. So the text is centred on the band's own
   axis exactly as it was, and the seal is lifted out of the flow into the right
   margin — 720px of head inside a 1240px wrap leaves 260px a side, and a 190px
   medallion sits in that with room to spare. */
body.koda-page-home .why-head{
  position: relative; min-height: 196px;
  display: flex; flex-direction: column; justify-content: center;
  margin-bottom: 42px;
}
body.koda-page-home .why-head .sec-head{ margin-bottom: 0; }
/* The width tracks the margin it is sitting in rather than being one number:
   720px of head inside the wrap leaves (wrap - 720)/2 a side, which is 260px at
   full width and 190px at the breakpoint below. 15vw lands inside both. */
body.koda-page-home .band-night .seal-stamp{
  position: absolute; right: 0; top: 50%; margin: 0;
  width: clamp(158px, 15vw, 196px);
  transform: translateY(-50%);
}
/* Below 1140px the margin is narrower than the medallion and there is no
   "beside" to be — the seal goes back to a centred medallion under the text,
   which is the one arrangement that still reads as deliberate at that width. */
@media (max-width: 1140px){
  body.koda-page-home .why-head{ min-height: 0; gap: 22px; }
  /* `transform: none` is LOAD-BEARING, not tidiness. The desktop rule above
     uses translateY(-50%) to sit the medallion on the headline's centre line;
     static flow does not need that and a seal shifted up by half its own height
     lands on top of the sub-line. This used to be reset by accident — the tilt
     that lived here overwrote the whole `transform` property — so when the tilt
     came out (2026-09-17) the reset had to be written down on purpose. */
  body.koda-page-home .band-night .seal-stamp{
    position: static; width: 168px; margin: 22px auto 0; transform: none;
  }
}
body.koda-page-home .band-night .seal-stamp-svg{
  filter:
    drop-shadow(0 3px 4px rgba(0,0,0,.45))
    drop-shadow(0 20px 38px rgba(0,0,0,.7));
}

/* ── THE SPEC STRIP ─────────────────────────────────────────────────────────
   Three facts, one object. The rules do the containing, so nothing sits in a
   box and the photograph runs underneath the whole thing.

   GILT RULES, STEEL LABELS. The horizontal rule at the top is gilt because a
   rule is decoration. The labels are NOT: Nocturne measures gilt at 2.37:1 on
   white and bars it for small text on a light ground, so a 12px gold cap on
   this scrim would be a contrast failure however much it suited the palette. */
body.koda-page-home .hero-facts{
  position: relative;
  display: grid; grid-template-columns: repeat(3, minmax(0, auto));
  justify-content: start;
  /* ═══ THE BOX IS THE THREE FACTS, NOT 600 PIXELS (2026-09-14) ═══
     It used to be a 600px box holding 441px of type, packed to the start —
     159 pixels of nothing on the right. Everything hung off that box was
     therefore centred on a phantom: the plate below, and the gold rule above.
     `width: max-content` makes the box exactly the three facts, so a symmetric
     inset is symmetric about the WORDS. `max-width` stays as the ceiling it
     always was. Reset at 640, where the strip deliberately stretches. */
  width: max-content;
  /* Centred on the same axis as the mark and the headline — see the note on
     .hero-copy. Below 640 the strip goes `width: auto` and fills the column, and
     an auto margin on a full-width box is a no-op, so this needs no reset. */
  margin-inline: auto;
  /* ═══ THE FEATHER IS AS LONG AS THE MARGIN IT HAS TO FADE INTO ═══
     A fixed 92px ramp is symmetric only while there are 92px to draw it in.
     `.wrap` is `min(1240px, 100% - 40px)`, so the gutter beside this strip is
     92px at 1440 and 20px at 1150 — and a 92px ramp on a 20px gutter is a ramp
     with 72px of itself off-screen, which arrives at the left edge at 78%
     opacity. That is a hard side, and a hard side on the left with a free fade
     on the right is exactly the asymmetry this was changed to fix.

     So the ramp is the gutter. 1256 = the 1240px wrap plus a 16px allowance for
     a classic scrollbar (100vw counts it, the layout does not), which keeps the
     fade finishing just INSIDE the frame rather than just outside it on the one
     browser that matters most here. The 14px floor is the ≤640 gutter, so the
     phone case falls out of the same expression and needs no override. */
  --fact-feather: clamp(14px, calc((100vw - 1256px) / 2), 92px);
  max-width: 600px; margin-top: 26px;
  padding: 16px 0 14px;
  border-top: 2px solid transparent;
  /* THE GOLD RULE IS CENTRED TOO. It used to run solid from 0 to 62% and fade
     out to the right — a left-anchored rule, which is exactly what made the
     whole strip read as pushed to one side. Now it is strongest in the middle
     and gives out evenly at both ends, on the same axis as the plate. */
  border-image: linear-gradient(90deg,
    rgba(201,164,76,0) 0%, var(--gold) 16%,
    var(--gold) 84%, rgba(201,164,76,0) 100%) 1;
  border-bottom: 1px solid var(--rule);
  border-left: 0; border-right: 0;
}
/* ═══ THE PLATE UNDER THE SPEC STRIP (2026-09-14) ═══════════════════════════
   Jeremy: *"Can you add a slight white overlay under the '6-12 hours dry time'
   to 'every surface one company' sections? We need those to pop a bit more
   without blanking out the rest of the photo."*

   THIS IS THE LOWEST LINE OF COPY IN THE HERO and it crosses the brightest,
   busiest part of the room — "Every Surface" lands on the sofa. The column
   veil (.hero-scrim) is deliberately thinnest down here so the furniture stays
   visible, and the white halo on the glyphs is a ring, not a ground: it saves
   a letter standing on a shadow, it does not make a strip of 12.5px tracked
   caps read as ONE object.

   SO THE STRIP GETS A GROUND OF ITS OWN, and every edge of it is feathered so
   it is a plate rather than a box:
     · the BACKGROUND feathers left and right, so it never reaches the booking
       card and never cuts a vertical line down the photograph;
     · the MASK feathers top and foot, so the gold rule above and the hairline
       below sit on it rather than on its edge.
   Four soft sides, one element, no box.

   ═══ AND IT WAS FEATHERED OFF-CENTRE (2026-09-14, later) ═══════════════════
   Jeremy: *"The backdrop for this section is perfect except it doesn't look
   properly placed / centered. We'll need to adapt this to make this look like
   the fade starts from the center and not left justified. Make sure it extends
   equally in both directions so everything appears centered."*

   HE IS DESCRIBING TWO DIFFERENT ASYMMETRIES AND THEY COMPOUNDED.

   ONE — THE FEATHERS WERE DIFFERENT LENGTHS. The left ramp was 26px and the
   right one ran from 74% to 100% of a 650px plate, which is 169. A 26px edge
   is a cut; a 169px edge is a fade. So the plate had a left EDGE and a right
   FADE, and an object with a hard side and a soft side reads as anchored to
   the hard one. Both are 92px now, measured in pixels rather than percent so
   the two stay identical at every width the strip can be.

   TWO — IT WAS CENTRED ON THE WRONG BOX. The insets hung off `.hero-facts`,
   which was 600px wide holding 441px of type packed to the start. The plate's
   own centre therefore sat 80px right of the words' centre while its left edge
   sat 24px left of them — so it was simultaneously too far right AND started
   too far left, which is precisely the "left justified" look. `.hero-facts` is
   `width: max-content` now (see above), so the box IS the words and a symmetric
   inset is symmetric about them.

   WHY 92px EACH SIDE, AND WHY THE PLATEAU. The ramp ends exactly where the type
   begins: full opacity runs from the first glyph of "6–12 Hours" to the last of
   "ONE COMPANY", and all 92px of each feather is spent OUTSIDE the words. That
   is what keeps this from costing legibility — every word still stands on .86
   white, the same as before, and it is only the empty margin either side that
   is now doing the dissolving. Measured at 1440: the plate runs 0.7 → 625 while
   the booking card starts at 819, so the right feather has 194px of clearance
   it did not have before.

   Z-INDEX -1, AND IT MATTERS WHICH CONTEXT IT LANDS IN. `.hero .wrap` is
   `position: relative; z-index: 2`, so it is a stacking context and this
   pseudo can only fall to the BOTTOM of it — behind .hero-facts' own border
   (the gold rule stays on top, which is the point) and still well above the
   scrim at z-index 1 and the photograph at 0. Put `isolation: isolate` on
   .hero-facts and the plate would paint OVER the gold rule instead.

   The blur is the difference between a wash and a plate: two pixels is enough
   that the room behind it goes quiet without going away. */
body.koda-page-home .hero-facts::before{
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  inset: -11px calc(-1 * var(--fact-feather)) -9px calc(-1 * var(--fact-feather));
  border-radius: 2px;
  background: linear-gradient(90deg,
    rgba(255,255,255,0)   0,
    rgba(255,255,255,.86) var(--fact-feather),
    rgba(255,255,255,.86) calc(100% - var(--fact-feather)),
    rgba(255,255,255,0)   100%);
  -webkit-backdrop-filter: blur(2px) saturate(1.04);
          backdrop-filter: blur(2px) saturate(1.04);
  -webkit-mask-image: linear-gradient(180deg,
    rgba(0,0,0,0) 0, #000 15px, #000 calc(100% - 13px), rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(180deg,
    rgba(0,0,0,0) 0, #000 15px, #000 calc(100% - 13px), rgba(0,0,0,0) 100%);
}
body.koda-page-home .hero-facts > div{
  padding: 0 22px;
  border-left: 1px solid rgba(201,164,76,.42);
}
body.koda-page-home .hero-facts > div:first-child{ padding-left: 0; border-left: 0; }
body.koda-page-home .hero-facts > div:last-child{ padding-right: 0; }
/* `text-shadow: inherit` on both tiers, deliberately restated: these are the
   glyphs Jeremy watched disappear ("the word 'every' gets lost"), and they are
   the ones furthest down the column, where the veil is thinnest. Inheriting is
   what gives them the ring defined on .hero-copy — without it they fall back to
   nothing and the ring stops at the sub-headline. */
body.koda-page-home .hero-facts dt{
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(18px, 1.5vw, 21px); line-height: 1.1;
  color: var(--hero-ink); letter-spacing: -.012em; white-space: nowrap;
  text-shadow: none;
}
body.koda-page-home .hero-facts dd{
  margin-top: 5px;
  font-size: 12.5px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  /* The strip is the lowest thing in the copy column, so it crosses the floor
     where the veil is thinnest — and at 12.5px with .13em of tracking these
     are the smallest glyphs standing on the photograph. Navy at 78% was chosen
     to match the tone of the steel labels elsewhere; matching a tone is not
     worth 22% of the contrast on the hardest surface the page has. Full ink. */
  color: var(--hero-ink); white-space: nowrap;
  text-shadow: none;
}

/* ═══ BELOW 1240 THE PENNANT IS IN THE PILL'S SEAT ════════════════════
   The gold review flag hangs 78px BELOW the bar (see THE TOP PADDING CLEARS THE
   FLAG), and the "Same KODA" pill sits in the first 33px of the hero — so on a
   wide screen they miss each other only because the flag is over by the phone
   number and the pill is over the copy column, 114px apart at 1440.

   THEY CONVERGE AS THE WINDOW NARROWS, AND THEY MEET AT 1240. Measured: the gap
   between the pill's right edge and the flag's left edge is 107px at 1280, 29px
   at 1240, 10px at 1202, and from there it is an overlap — 41px at 1100, and by
   1000, where the copy stops being a column and runs down the middle of the
   page, the flag comes down through the MIDDLE of the pill. No amount of
   narrowing the pill escapes that: below 1000 its centre and the flag's centre
   are the same centre.

   SO THE CLEARANCE IS VERTICAL, WHICH IS THE FIX THIS HERO ALREADY USES. The
   phone breakpoint has cleared the flag with top padding since the redesign
   (it was the only width where the flag hung over the copy); this is that same
   number, applied from the width where the problem actually starts rather than
   from 640. 92px puts the pill's top 14px below the flag's point, and its
   bottom stays 11px clear of the mark's first visible pixel even at the 400px
   mark size — the tightest case on the page.

   ABOVE 1240 NOTHING MOVES. The composition Jeremy tuned on 09-16 — the 56px
   lift that took the sub-line off the console table — is untouched at the
   widths it was tuned at, and the pill is free there because the masthead's
   negative margin pays for it out of the mark's transparent canvas. */
@media (max-width: 1240px) {
  body.koda-page-home .hero{ padding-top: calc(var(--bar-h) + 92px); }
}
@media (max-width: 1000px) {
  body.koda-page-home .hero-grid{ grid-template-columns: 1fr; gap: 30px; }
  body.koda-page-home .hero-sub{ max-width: 46ch; }
  /* Below 1000px the copy stacks above the booking card and runs down the
     middle, so the left-weighted veil no longer covers it. Same inverted
     treatment, turned through ninety degrees: opaque white behind the copy,
     thinning over the lower half where the photograph can show. */
  /* ═══ THE PHOTOGRAPH BECOMES A BAND ═══
     Stacked, the hero is the copy AND the whole booking card — well over
     2,000px on a phone. `background-size: cover` over a 375 × 2000 box crops a
     landscape photograph to a vertical sliver: the first mobile pass rendered
     a bright living room as one indistinct pale wall, and no amount of work on
     the veil would have fixed it, because there was nothing behind the veil.

     So on a narrow screen the photograph stops. It is a band across the top,
     tall enough to hold the copy and no taller, at an aspect a room can
     actually be seen in — and the veil ends by resolving INTO the page colour,
     so the band dissolves rather than stopping at a line. The card below sits
     on paper, which is where a form belongs anyway. */
  body.koda-page-home .hero{ --hero-band: 760px; }
  body.koda-page-home .hero-scrim{
    background:
      linear-gradient(180deg,
        rgba(255,255,255,.62) 0%, rgba(255,255,255,.57) 44%,
        rgba(255,255,255,.30) 62%, rgba(255,255,255,.12) 76%,
        var(--page) 100%),
      linear-gradient(180deg, rgba(22,32,46,.12) 0%, rgba(22,32,46,0) 12%);
  }
  /* Higher up the frame than the desktop crop: stacked, the copy sits over the
     top of the photograph rather than beside it, and 68% down a narrow crop is
     a close-up of a rug with nothing to say. */
  body.koda-page-home .hero-photo{ background-position: center 46%; }
}
@media (max-width: 640px) {
  /* 92, not the 88 this breakpoint carried before the "Same KODA" pill: it is
     now the same clearance the 1240 rule above uses, so the pill sits the same
     14px below the flag's point on a phone as it does on a laptop. */
  body.koda-page-home .hero{ padding: calc(var(--bar-h) + 92px) 0 36px; --hero-band: 700px; }
  /* THE SPEC STRIP HAS TO WRAP. Three nowrap columns at 375px ran "Every
     Surface / ONE COMPANY" clean off the right edge — and the labels cannot be
     shrunk out of trouble, because 12.5px is already at CLAUDE.md §4.1's floor
     for a page whose audience skews 35+. So it becomes two columns and the
     third takes a row of its own, which is a layout rather than an overflow. */
  body.koda-page-home .hero-facts{
    grid-template-columns: repeat(2, minmax(0, 1fr));
    /* `width: auto` undoes the desktop shrink-wrap. Here the strip deliberately
       fills the column — two columns of 1fr have nothing to shrink to — and the
       plate's symmetric feather stays symmetric because the box is symmetric. */
    justify-content: stretch; width: auto; max-width: none;
    row-gap: 13px;
  }
  body.koda-page-home .hero-facts > div{ padding: 0 0 0 16px; }
  body.koda-page-home .hero-facts > div:nth-child(odd){ padding-left: 0; border-left: 0; }
  body.koda-page-home .hero-facts > div:last-child{
    grid-column: 1 / -1; padding: 12px 0 0;
    border-left: 0; border-top: 1px solid rgba(201,164,76,.42);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE BOOKING CARD
   The single most important object on the site: a raised sheet of paper with a
   navy header band, stepping through what → details → when → you → the price.

   ═══ EVERY STEP FITS IN ABOUT 480px ═══

   That is the whole brief, and it is why the controls below look the way they
   do. Nine stepper rows became nine tiles three across. Four floor questions
   became one, with the other three behind the answer that needs them. Twenty-one
   day tiles in an inner scrollbar became a calendar. None of it is compression
   for its own sake — each one is also the clearer control — but the height is
   what decided which clearer control to reach for.
   ═══════════════════════════════════════════════════════════════════════════ */
/* NOTE ON THE GRAIN. It never goes on a light surface as a background-image —
   the tile is 50%-opacity noise, and painted straight onto white it is just a
   grey card. It belongs on a pseudo-element at single-digit opacity (the bar,
   the ink bands, the navy header below), where it reads as ink on paper. */
body.koda-page-home .book{
  background: var(--paper);
  border-radius: var(--radius); overflow: hidden;
  box-shadow: var(--lift-4);
  border: 1px solid rgba(22,40,63,.1);
  position: relative;
}
/* A foil edge along the top, tying the card to the medal in the bar. */
body.koda-page-home .book::before{
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; z-index: 3;
  background: linear-gradient(90deg, var(--gold-deep), var(--gold-lit) 45%, var(--gold) 70%, var(--gold-deep));
}
body.koda-page-home .book-head{
  background: linear-gradient(178deg, var(--ink-lift), var(--ink-mid) 60%, var(--ink));
  padding: 17px 26px 15px; color: #fff; position: relative;
}
body.koda-page-home .book-head::after{
  content: ''; position: absolute; inset: 0; background-image: var(--grain);
  opacity: .1; mix-blend-mode: overlay; pointer-events: none;
}
body.koda-page-home .book-head-top{
  position: relative; z-index: 1;
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
}
/* THE SEAL OWNS THIS CORNER, SO THE HEADER STOPS USING IT.
   The seal is 176px wide and hangs 22px past the card's right edge, which puts
   its face over the right 154px of this row — and what was under it is "No card
   needed", the one line on this header that answers the question a stranger has
   before they will type their phone number into anything.

   RESERVING THE WIDTH DID NOT WORK, AND THE ARITHMETIC IS WHY. The title is
   216px on one line and the note is 134px; with 14px between them that is 364
   of the row's 476, leaving 112px to give away, against a mark that needs 154.
   Padding the row enough to clear the seal wrapped "Book Your Cleaning" onto
   two lines, which is a worse header than the one being fixed.

   SO THEY STACK. Title over note, both left, both short enough (216px and
   134px) to sit entirely clear of a seal whose face does not begin until 376px
   across. It costs the header 25px of height and reads better than it did
   flat — a promise under a title is a promise about the title. Below 1000px
   the seal is not on this corner at all (see .seal-stamp) and the row goes back
   to being a row. */
@media (min-width: 1001px){
  body.koda-page-home .book-head-top{
    flex-direction: column; align-items: flex-start; gap: 4px;
  }
}
body.koda-page-home .book-head h2{ color: #fff; font-size: 25px; margin-bottom: 0; }
body.koda-page-home .book-head-note{
  font-size: 13px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: rgba(240,216,148,.92); white-space: nowrap;
}

/* ── THE RAIL, NAMED ────────────────────────────────────────────────────────
   Four ticks and, under them, which one you are on and what it is called. The
   ticks alone answered "how far through am I" and nothing else; a step that
   says "Step 3 of 4 · When" is an itinerary rather than a progress bar, and it
   is the cheapest possible way to tell somebody the form is nearly over. */
body.koda-page-home .steps{ position: relative; z-index: 1; margin-top: 13px; }
/* ── AND THE RAIL HOLDS ITS ROOM TOO (2026-09-16) ─────────────────────
   The step rail ships `hidden` because how many steps there are is a fact about
   the form, and home.js is what knows it. That was the other half of the
   load-time jump described under THE PLACEHOLDER RESERVES THE ROOM below: the
   card's head was 51px shorter until the catalog arrived, and the body moved
   from y 216 to y 267 in the same recorded shift.

   THE ELEMENT ITSELF CANNOT BE MADE TO HOLD THE ROOM. Chrome's UA sheet is
   `[hidden]:not([hidden="until-found"]) { display: none !important }`, so an
   author `display: block` loses; tried, measured, `display` came back `none`
   while the height and visibility beside it applied. Fighting that with
   `!important` would also mean the rail kept its band on the ESTIMATE step,
   where home.js hides it again on purpose (stepsEl.hidden = true) and the space
   would be a hole rather than a reservation.

   So the head reserves it, and only while the form has not arrived. The
   skeleton's presence is exactly that condition and it is removed with the rest
   of the placeholder, so the padding lifts by itself and the rule can never
   apply to a later hide. 51 = the rail's 38 plus the 13 margin above it. */
body.koda-page-home .book:has(#bookSkeleton) .book-head{ padding-bottom: 66px; }
@media (max-width: 560px){
  body.koda-page-home .book:has(#bookSkeleton) .book-head{ padding-bottom: 64px; }
}
body.koda-page-home .steps-bar{ display: flex; gap: 6px; }
body.koda-page-home .steps-bar i{
  height: 4px; flex: 1; border-radius: 3px; background: rgba(255,255,255,.2);
  transition: background .35s var(--ease);
}
body.koda-page-home .steps-bar i.done{ background: rgba(240,216,148,.55); }
body.koda-page-home .steps-bar i.now{ background: linear-gradient(90deg, var(--gold-lit), var(--gold)); box-shadow: 0 0 10px rgba(240,216,148,.5); }
body.koda-page-home .steps-now{
  display: flex; gap: 9px; align-items: baseline; margin-top: 8px;
  font-size: 13px; letter-spacing: .06em;
}
body.koda-page-home .steps-now #stepNum{
  font-weight: 700; text-transform: uppercase; letter-spacing: .13em;
  color: rgba(255,255,255,.62);
}
body.koda-page-home .steps-now #stepName{
  font-family: var(--font-display); font-size: 15px; font-weight: 700;
  letter-spacing: 0; color: #fff;
}

body.koda-page-home .book-body{ padding: 22px 26px 24px; }
@media (max-width: 560px) {
  body.koda-page-home .book-head{ padding: 15px 18px 13px; }
  body.koda-page-home .book-body{ padding: 18px 18px 20px; }
  /* On a phone the bar's gold pennant hangs over the card's top-right corner,
     which is exactly where this sits — and it is the least load-bearing thing
     on the card. It goes rather than fighting the flag, and the title gets its
     one line back. */
  body.koda-page-home .book-head-note{ display: none; }
}
body.koda-page-home .step[hidden]{ display: none; }
body.koda-page-home .step-title{ font-family: var(--font-display); font-size: 22px; color: var(--ink); margin-bottom: 3px; }
body.koda-page-home .step-hint{ font-size: 15px; color: var(--ink-soft); margin-bottom: 14px; line-height: 1.45; }

/* ═══ THE INTAKE GRID (2026-09-10) ═════════════════════════════════════════
   Jeremy: *"show them a grid with the first column being the room/upholstery
   item… the next column is the room name… then the floor… Then add the addon
   columns (with 'apply to all' style options for the addons as well)."*

   THE WHOLE POINT IS THAT THE ANSWERS LINE UP. The step it replaces was one
   card per area, stacked — six rooms was six 300px cards asking the same three
   questions in the same order, 1,800px of form, and no way to check "is the
   protector on everything upstairs?" without scrolling and remembering. In
   columns that question is answered by looking down one.

   ═══ IT IS A CSS GRID, NOT A <table>, AND THAT IS DELIBERATE ═══

   A table cannot restack. Below 860px this becomes one CARD PER LINE — the
   header disappears, every cell grows a label out of its own `data-label`, and
   the row reads top to bottom. A <table> would have needed a second copy of the
   markup to do that, and two copies of a form is how a form starts lying.

   ═══ THE COLUMN COUNT IS DATA ═══

   `--gcols` is how many add-ons the live catalog is currently offering, written
   by repaintGrid() from offeredAddons(). The header and every row read the same
   property, so a fifth add-on appearing in the catalog widens the grid and
   nothing here needs editing. Hardcoding four columns is how a form and its
   server drift apart. */
body.koda-page-home .grid{
  --gcols: 4;
  margin: 0 0 22px;
}
body.koda-page-home .ghead,
body.koda-page-home .grow{
  display: grid;
  /* what · area · floor · N add-ons · the remove button */
  grid-template-columns:
    minmax(120px, 1fr) minmax(150px, 1.25fr) minmax(120px, 1fr)
    repeat(var(--gcols), minmax(84px, 84px))
    34px;
  gap: 8px;
  align-items: center;
}
/* AND WITHOUT THE FIRST ONE. "What it is" is a question only an upholstery row
   asks, so a grid of carpet rooms drops the column entirely and gives the width
   back to the two pickers that are doing the work — see needsItemCol() in
   home.js, which sets this class and is the single source of the column count
   for both the header and the rows. */
body.koda-page-home .grid.no-item .ghead,
body.koda-page-home .grid.no-item .grow{
  grid-template-columns:
    minmax(170px, 1.3fr) minmax(130px, 1fr)
    repeat(var(--gcols), minmax(84px, 84px))
    34px;
}
body.koda-page-home .ghead{
  padding: 0 10px 9px;
  border-bottom: 1.5px solid var(--rule-firm);
  margin-bottom: 6px;
}
/* 13px is a COLUMN HEAD, not body copy — §4.1's 14px floor governs secondary
   text and this is a label whose content is the 16px control under it. It is
   full ink rather than muted, which is where the readable weight comes from. */
body.koda-page-home .ghead .gcell{
  font-family: var(--font-body); font-size: 13px; font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink);
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px;
}
body.koda-page-home .ghead .gc-add{ align-items: center; text-align: center; }
body.koda-page-home .ghead-lbl{ line-height: 1.25; }

/* ── APPLY TO ALL ──
   Sits under its column's name because a control that says "all" has to be at
   the top of the thing it is all of. Three states to READ (none / some / every)
   and two to WRITE — pressing it always either fills the column or empties it,
   so the next state is never a guess. */
body.koda-page-home .gall{
  /* 13px, not the 11.5px the pennant's LABELS use: this one is a control a
     customer presses, and CLAUDE.md 4.1 is a rule about a 35+ audience finding
     things. It is still the smallest interactive thing on the step, which is
     right for a column head, and its hit area is the whole pill. */
  font-family: var(--font-body); font-size: 13px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px; cursor: pointer;
  color: var(--steel-deep); background: var(--paper);
  border: 1px solid var(--rule-firm);
  transition: background .16s, color .16s, border-color .16s;
}
body.koda-page-home .gall:hover:not(:disabled){ border-color: var(--ink); color: var(--ink); }
body.koda-page-home .gall.part{ border-color: var(--steel); color: var(--ink); background: var(--steel-soft); }
body.koda-page-home .gall.on{ background: var(--ink); border-color: var(--ink); color: #fff; }
body.koda-page-home .gall:disabled{ opacity: .4; cursor: not-allowed; }

body.koda-page-home .grow{
  padding: 9px 10px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  transition: background .18s, border-color .18s;
}
/* Zebra by position, not by state: the eye tracks a long row across a wide
   grid on the stripe, and a stripe that appears when a row is answered would
   make the table flicker as it is filled in. */
body.koda-page-home .grow:nth-child(odd){ background: rgba(30,37,48,.028); }
body.koda-page-home .grow:hover{ background: rgba(96,120,152,.09); }
/* THE ONE PLACE A ROW IS COLOURED. Not when it is finished — most rows are
   finished most of the time, and a grid of green rows says nothing. Only when
   Continue has been pressed with something missing, and only on the row that is
   missing it. */
body.koda-page-home .grow.asking{
  border-color: var(--act); background: rgba(139,49,42,.06);
}
/* THE SPARE ROW IS AN INVITATION, NOT AN UNFINISHED LINE. It is the empty row
   the grid always keeps at the bottom (see THE SPARE ROW in home.js): the
   customer did not put it there and is never asked to finish it, so it must not
   look like the rows that are theirs. A dashed hairline and a little less ink
   is the whole treatment — enough to read as "next one goes here", not enough
   to read as a row waiting on an answer. It picks up full weight the moment
   anything is chosen in it, because at that point it stops being spare. */
body.koda-page-home .grow.spare{
  border: 1px dashed var(--rule-firm);
  background: transparent;
}
body.koda-page-home .grow.spare:hover{ background: rgba(96,120,152,.06); }
body.koda-page-home .grow.spare .gsel{ color: var(--muted); }

body.koda-page-home .gcell{ min-width: 0; }
body.koda-page-home .gc-add{ display: flex; justify-content: center; }
body.koda-page-home .gc-kill{ display: flex; justify-content: flex-end; }

/* ── THE PICKERS ──
   A <select> rather than a row of chips, because seven columns of chips is a
   wall and a wall is what gets scrolled past — and because the room list is
   fifteen options long, which a chip row cannot hold and an <optgroup> can.
   White ground: Jeremy's standing rule for this form is "input fields be white
   background to ensure they can be seen with old eyes". */
body.koda-page-home .gsel{
  width: 100%; font-family: var(--font-body); font-size: 16px;
  color: var(--text); background: var(--paper);
  border: 1.5px solid var(--rule-firm); border-radius: var(--radius-sm);
  padding: 9px 28px 9px 10px;
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235f646c' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center;
  cursor: pointer;
  transition: border-color .16s, box-shadow .16s;
}
body.koda-page-home .gsel:hover{ border-color: var(--ink); }
body.koda-page-home .gsel:focus{ outline: none; border-color: var(--steel); box-shadow: 0 0 0 3px rgba(96,120,152,.22); }
/* An unanswered cell says so quietly — a dashed edge and the placeholder in
   steel. Loud enough to find on a scan, not loud enough to read as an error on
   a line the customer added four seconds ago. */
body.koda-page-home .gsel.empty{
  border-style: dashed; color: var(--steel-deep);
  background-color: rgba(96,120,152,.05);
}

/* ── THE TICK ──
   A button, not a checkbox: it is 30px square in a 84px column and it has to be
   hittable with a thumb. `aria-pressed` carries the state. */
body.koda-page-home .gtick{
  width: 30px; height: 30px; border-radius: var(--radius-sm); cursor: pointer;
  display: grid; place-items: center;
  background: var(--paper); border: 1.5px solid var(--rule-firm);
  transition: background .15s, border-color .15s, transform .12s;
}
body.koda-page-home .gtick svg{ opacity: 0; transition: opacity .12s; }
body.koda-page-home .gtick:hover{ border-color: var(--ink); }
body.koda-page-home .gtick.on{
  background: linear-gradient(180deg, var(--ink-lift), var(--ink));
  border-color: var(--ink);
}
body.koda-page-home .gtick.on svg{ opacity: 1; }
body.koda-page-home .gtick:active{ transform: scale(.92); }
/* NOT ELIGIBLE IS NOT UNTICKED. An empty box says "you could have this and
   chose not to", which would be a lie about carpet protector on a mattress. */
body.koda-page-home .gdash{ color: var(--rule-firm); font-size: 16px; user-select: none; }

body.koda-page-home .gkill{
  width: 28px; height: 28px; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center;
  background: none; border: 0; color: var(--muted);
  transition: background .15s, color .15s;
}
body.koda-page-home .gkill:hover{ background: rgba(200,56,44,.12); color: #a3271e; }

/* ── ADD A LINE ──
   The two buttons Jeremy named, and they are the loudest thing on an empty
   step because on an empty step they are the only thing to do. */
body.koda-page-home .gadd{ display: flex; flex-wrap: wrap; gap: 10px; margin: 4px 0 16px; }
body.koda-page-home .gadd-btn{
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-body); font-size: 16px; font-weight: 700;
  color: var(--ink); background: linear-gradient(180deg, #fff, var(--page-deep));
  border: 1.5px solid var(--rule-firm); border-radius: 999px;
  padding: 11px 20px 11px 16px; cursor: pointer;
  box-shadow: var(--lift-1), inset 0 1px 0 #fff;
  transition: transform .15s var(--ease), box-shadow .15s, border-color .15s;
}
body.koda-page-home .gadd-btn svg{ color: var(--steel-deep); }
body.koda-page-home .gadd-btn:hover{ transform: translateY(-2px); box-shadow: var(--lift-2); border-color: var(--ink); }
body.koda-page-home .gadd-btn:active{ transform: translateY(1px); box-shadow: var(--press); }

body.koda-page-home .gtally{
  font-size: 15px; font-weight: 600; color: var(--gold-deep); margin-bottom: 10px; min-height: 1.2em;
}
body.koda-page-home .gtally.ok{ color: #1f8a5b; }
body.koda-page-home .gempty{
  font-size: 16px; color: var(--ink-soft); padding: 26px 14px; text-align: center;
  border: 1.5px dashed var(--rule-firm); border-radius: var(--radius);
  background: rgba(96,120,152,.05);
}

/* ── ONE CARD PER LINE, BELOW 860px ────────────────────────────────────────
   A seven-column grid on a phone is not a grid, it is a horizontal scrollbar.
   So the header goes, and every cell grows the label the header was carrying —
   which is why every .gcell in the row markup ships a `data-label`. Nothing is
   duplicated: the same DOM reads as a table on a desktop and as a card on a
   phone. */
@media (max-width: 860px){
  body.koda-page-home .ghead{ display: none; }
  body.koda-page-home .grow{
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px 10px;
    padding: 16px 14px;
    border: 1.5px solid var(--rule);
    background: var(--paper) !important;
    box-shadow: var(--lift-1);
    margin-bottom: 12px;
    position: relative;
  }
  body.koda-page-home .grow .gcell{ display: flex; flex-direction: column; align-items: stretch; gap: 5px; }
  body.koda-page-home .grow .gcell::before{
    content: attr(data-label);
    font-size: 12.5px; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--ink-soft);
  }
  /* The first two answers get the full width — "Which room" holds the longest
     strings in the form and is the one that must not be truncated. */
  body.koda-page-home .grow .gc-item,
  body.koda-page-home .grow .gc-room,
  body.koda-page-home .grow .gc-floor{ grid-column: 1 / -1; }
  /* Stacked, a blank cell is a blank LINE with a heading over it. The carpet
     row's placeholder exists to hold a column open; with no columns to hold
     open it is nothing, so it is not drawn. */
  body.koda-page-home .grow .gc-item-blank{ display: none; }
  body.koda-page-home .grow .gc-add{ align-items: flex-start; }
  body.koda-page-home .grow .gc-add .gdash{ padding-left: 2px; }
  /* The remove control leaves the flow and sits in the card's own corner. */
  body.koda-page-home .grow .gc-kill{
    position: absolute; top: 8px; right: 8px; grid-column: auto;
  }
  body.koda-page-home .grow .gc-kill::before{ content: none; }
}

/* ── THE CARD TAKES THE WIDTH THE STEP NEEDS ───────────────────────────────
   Set by setStep() in home.js. Three states, one per shape of question:

     (none)  step 1, the contact form — 530px beside the hero copy, unchanged
     .solo   the copy column steps aside; the card is on its own
     .full   AND the card takes the whole hero, because a seven-column grid
             does not fit in 530px and there is no honest way to make it

   THE COPY DOES NOT COME BACK once the customer has committed. It reappears
   only if they walk back to step 1, which is the one place it is still selling
   anything. Gated above 1000px because below that the hero is stacked and the
   card is already full width. */
@media (min-width: 1000px){
  body.koda-page-home .hero-grid.solo{
    grid-template-columns: minmax(0, 1fr);
  }
  body.koda-page-home .hero-grid.solo .hero-copy{ display: none; }
  /* Not the grid step: a calendar and a receipt want a measure, not a hall.
     760px is the width the calendar's five weekday columns were drawn for. */
  body.koda-page-home .hero-grid.solo .book{ max-width: 760px; margin-inline: auto; }
  body.koda-page-home .hero-grid.solo.full .book{ max-width: none; }
}
/* The grid step's own pane can breathe a little wider inside the card. */
body.koda-page-home .step-grid{ max-width: 1180px; margin-inline: auto; }

/* The contact step is the first thing anyone sees inside the card and it has no
   Back button, so its one control goes to the right on its own. */
body.koda-page-home .nav-row-solo{ justify-content: flex-end; }

/* The recap's Cleaning row is one line per floor. Blocks rather than <br>s, so
   a long floor wraps under itself rather than under the floor above it. */
body.koda-page-home .recap-lines span{ display: block; }
body.koda-page-home .recap-lines span + span{ margin-top: 3px; }

/* ── The worries ────────────────────────────────────────────────────────────
   Jeremy: "Find a better way to display the 'anything we should know' pills so
   they are more visually appealing (right now they are stacked weirdly on the
   left with deadspace to the right)."

   They were inline lozenges in a wrapping flex row, so their width came from
   the length of their words: "Pet odor or stains" and "Allergies or asthma"
   filled a line, "A specific stain" and "Heavy traffic lanes" nearly did, and
   "Moving in or out" sat alone against a third of a line of nothing. A ragged
   right edge is fine in a paragraph and wrong in a set of equal choices, where
   the eye reads the ragged edge as meaning — as if the short one mattered less.

   A GRID, NOT A FLOW. Equal columns, so five equal choices look like five equal
   choices, and an odd one out takes the full width of the last row rather than
   leaving a hole beside it. They are the same object as the extras on the cards
   above — white, ringed, raised, with the same square tick, filling with the
   same steel wash — because they ask the same kind of question. */
body.koda-page-home .chips{
  display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr));
  gap: 7px; align-items: stretch;
}
/* ── AND THE BOX THE HEADING WAS PROMISING ──
   The block asks "anything we should know?" and until now offered six
   tick-boxes as the entire answer. The chips are the worries we can NAME; this
   is everything else, and it is the field somebody types "the stain is under
   the piano" into. It sits UNDER the chips rather than above them because the
   cheap answer should come first: a customer who only has to tap "Pet odor"
   should never have had to scroll past an empty textarea to reach it. */
body.koda-page-home .blk-note{ margin-top: 11px; }
body.koda-page-home .blk-note > span{
  display: block; font-size: 14.5px; font-weight: 600; color: var(--ink-soft); margin-bottom: 5px;
}
body.koda-page-home .blk-note .opt{
  display: inline; font-size: 13px; font-weight: 600; color: var(--steel-deep);
}
body.koda-page-home .blk-note textarea{
  width: 100%; font-family: var(--font-body); font-size: 16px; line-height: 1.5;
  color: var(--text); background: var(--paper);
  border: 1.5px solid var(--rule-firm); border-radius: var(--radius-sm);
  padding: 10px 12px; min-height: 74px; resize: vertical;
  transition: border-color .16s, box-shadow .16s;
}
body.koda-page-home .blk-note textarea::placeholder{ color: rgba(30,37,48,.42); }
body.koda-page-home .blk-note textarea:focus{
  outline: none; border-color: var(--steel); box-shadow: 0 0 0 3px rgba(96,120,152,.22);
}
/* The last row's orphan spans it rather than sitting in a half-empty row. */
body.koda-page-home .chips > .chip:last-child:nth-child(odd){ grid-column: 1 / -1; }
body.koda-page-home .chip{
  position: relative; display: flex; align-items: center; gap: 10px;
  font-size: 14.5px; font-weight: 600; color: var(--text);
  background: var(--paper); border: 1.5px solid var(--rule-firm);
  border-radius: var(--radius-sm); padding: 10px 13px; min-height: 44px; cursor: pointer;
  box-shadow: var(--lift-1), inset 0 1px 0 #fff;
  line-height: 1.3;
  transition: background .16s, border-color .16s, box-shadow .16s;
}
body.koda-page-home .chip input{ position: absolute; opacity: 0; pointer-events: none; }
body.koda-page-home .chip:hover{ border-color: var(--steel); box-shadow: var(--lift-2), inset 0 1px 0 #fff; }
body.koda-page-home .chip:has(input:checked){
  background: linear-gradient(180deg, #fff, var(--steel-soft));
  border-color: var(--steel);
  box-shadow: var(--lift-2), inset 0 1px 0 #fff;
}
body.koda-page-home .chip .tick{
  width: 20px; height: 20px; border-radius: 5px; flex-shrink: 0;
  border: 1.5px solid var(--rule-firm); background: var(--page-deep);
  box-shadow: var(--press);
  display: grid; place-items: center;
  transition: background .16s, border-color .16s;
}
body.koda-page-home .chip:has(input:checked) .tick{ background: var(--steel); border-color: var(--steel-deep); box-shadow: none; }
body.koda-page-home .chip .tick svg{ opacity: 0; transition: opacity .18s; }
body.koda-page-home .chip:has(input:checked) .tick svg{ opacity: 1; }
body.koda-page-home .chip:focus-within{ outline: 3px solid var(--steel); outline-offset: 2px; }
@media (max-width: 400px){
  body.koda-page-home .chips{ grid-template-columns: 1fr; }
}

/* ── STEP 3: THE CALENDAR ───────────────────────────────────────────────────
   Jeremy: "giant calendar date buttons with no timeslots displaying, no
   separation between weeks, no intention or thoughtfulness behind it."

   It is a calendar now. Five columns, Monday to Friday; a day sits in its own
   weekday column whether or not there is a day to its left, so a ROW IS A WEEK
   and "the Tuesday after next" is a place on a grid rather than the eleventh
   tile in a run. A month caption spans the grid where the month turns. Three
   weeks of days fit in 200px, where twenty-one 74px tiles needed an inner
   scrollbar inside a card that was already too tall to see. */
body.koda-page-home .leadnote{
  display: flex; gap: 10px; align-items: flex-start;
  background: linear-gradient(180deg, #fff, var(--steel-soft));
  border: 1px solid rgba(63,110,160,.3); border-left: 4px solid var(--steel);
  border-radius: var(--radius-sm); padding: 11px 14px; margin-bottom: 15px;
  box-shadow: var(--lift-1);
}
body.koda-page-home .leadnote svg{ flex-shrink: 0; margin-top: 2px; }
body.koda-page-home .leadnote p{ font-size: 14.5px; line-height: 1.45; color: var(--text); }
body.koda-page-home .leadnote a{ color: var(--ink); font-weight: 700; white-space: nowrap; }

body.koda-page-home .cal{
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 3px;
  border-radius: var(--radius); padding: 4px;
  transition: box-shadow .2s;
}
body.koda-page-home .cal.bad{ box-shadow: 0 0 0 3px #c8382c; }
body.koda-page-home .cal-dow{
  text-align: center; padding-bottom: 4px;
  /* 13px, not 12. The column heads are five across a 480px card with room to
     spare, and CLAUDE.md §4.1 puts the absolute floor at 12px for an audience
     that skews 35+ — sitting exactly on a floor is not the same as clearing it. */
  font-size: 13px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-soft);
}
/* The month turns. Spanning the whole grid is what makes it a caption rather
   than a cell, and it is what stops a run of days reading as one long month. */
body.koda-page-home .cal-month{
  grid-column: 1 / -1; margin: 7px 0 1px; padding-bottom: 5px;
  font-family: var(--font-display); font-size: 13px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--steel-deep);
  border-bottom: 1px solid var(--rule-soft);
}
body.koda-page-home .cal-dow + .cal-month,
body.koda-page-home .cal-month:first-of-type{ margin-top: 0; }
body.koda-page-home .cal-day{
  position: relative; cursor: pointer; text-align: center;
  min-height: 38px; padding: 8px 2px 7px; border-radius: var(--radius-sm);
  border: 1.5px solid var(--rule); background: var(--paper);
  box-shadow: var(--lift-1), inset 0 1px 0 #fff;
  transition: transform .13s var(--ease), box-shadow .13s, border-color .13s, background .16s;
}
body.koda-page-home .cal-day input{ position: absolute; opacity: 0; pointer-events: none; }
body.koda-page-home .cal-n{
  display: block; font-family: var(--font-display); font-size: 17px; font-weight: 700;
  line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums;
}
body.koda-page-home .cal-day:hover{ transform: translateY(-1px); box-shadow: var(--lift-2); border-color: var(--steel); }
body.koda-page-home .cal-day:has(input:checked){
  background: linear-gradient(178deg, var(--ink-lift), var(--ink));
  border-color: var(--ink-deep); box-shadow: var(--lift-3);
}
body.koda-page-home .cal-day:has(input:checked) .cal-n{ color: #fff; }
body.koda-page-home .cal-day:focus-within{ outline: 3px solid var(--steel); outline-offset: 2px; }
/* A day the fleet is already at capacity on. Dimmed and dotted, never removed:
   the office can make room, and a picker showing nothing at all just loses the
   customer. */
body.koda-page-home .cal-day.full{ opacity: .48; }
body.koda-page-home .cal-dot{
  display: block; width: 4px; height: 4px; border-radius: 50%;
  background: var(--gold-deep); margin: 4px auto 0;
}
body.koda-page-home .cal-day:has(input:checked) .cal-dot{ background: var(--gold-lit); }
/* The later weeks. They are in the grid already — folding them is a class, so
   opening them costs nothing and keeps every day in its weekday column. */
body.koda-page-home .cal-later{ display: none; }
body.koda-page-home .cal-more{
  display: block; width: 100%; margin-top: 8px; padding: 10px 12px;
  background: var(--page-deep); border: 1.5px dashed var(--rule-firm);
  border-radius: var(--radius-sm); cursor: pointer;
  font: inherit; font-size: 14.5px; font-weight: 600; color: var(--steel-deep);
  transition: background .16s, border-color .16s;
}
body.koda-page-home .cal-more:hover{ background: var(--steel-soft); border-color: var(--steel); }
body.koda-page-home .cal-more:focus-visible{ outline: 3px solid var(--steel); outline-offset: 2px; }

body.koda-page-home .cal-legend{
  display: flex; align-items: flex-start; gap: 7px;
  font-size: 13.5px; color: var(--ink-soft); margin: 9px 2px 0; line-height: 1.4;
}
/* Pinned to the FIRST line, not the middle of the block: on a phone this wraps
   to two lines, and a centred dot then points at the gap between them. */
body.koda-page-home .cal-legend .cal-dot{ margin: 7px 0 0; flex-shrink: 0; }

/* ── ARRIVAL WINDOWS ────────────────────────────────────────────────────────
   Drawn for the DAY THAT WAS PICKED, from that day's own am_full / pm_full, and
   they do not exist before one is chosen. Buttons offering times on no
   particular date is the thing that made the old picker feel unconsidered.

   A FULL WINDOW IS DEAD, NOT DIM (2026-09-10). It used to be drawn at half
   opacity, labelled "Looking full", and still selectable — on the theory that
   the office confirms every request and can usually make room. Jeremy: "We
   can't present it as an option if unavailable." A greyed thing a customer can
   still tap and still be emailed about is an offer, whatever the label says.
   So it is disabled, struck through, and cannot be the selection; if both go,
   renderWindows replaces the whole panel with the day's actual answer. */
/* The windows are the first .blk in their pane, so the .blk + .blk rule that
   separates stacked questions does not reach them. They still need the break —
   the calendar above ends in a legend line, and without a rule the two run
   together as one paragraph. */
body.koda-page-home #winPanel{ margin-top: 16px; padding-top: 15px; border-top: 1px solid var(--rule-soft); }
body.koda-page-home .wins{ display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
/* "I'm Flexible This Day" IS NOT A THIRD MORNING. Jeremy: "Can you spread 'I'm
   Flexible this day' between 'morning' and 'afternoon' so it's a longer button
   that sits cleanly below both?" It is not a third time of day — it is the
   answer that covers both of the ones above it, and as a half-width tile in the
   left column it read as a stray option with a hole beside it. Spanning the row
   says what it means: everything above this. */
body.koda-page-home .win-any{ grid-column: 1 / -1; }
body.koda-page-home .win{
  position: relative; cursor: pointer; padding: 10px 12px;
  border: 1.5px solid var(--rule-firm); border-radius: var(--radius); background: var(--paper);
  box-shadow: var(--lift-1), inset 0 1px 0 #fff; transition: all .16s var(--ease);
}
body.koda-page-home .win input{ position: absolute; opacity: 0; pointer-events: none; }
body.koda-page-home .win-name{
  display: block; font-family: var(--font-display); font-size: 16px; font-weight: 700;
  color: var(--ink); line-height: 1.2;
}
body.koda-page-home .win small{ display: block; margin-top: 2px; font-size: 13.5px; color: var(--ink-soft); line-height: 1.3; }
body.koda-page-home .win:hover{ border-color: var(--steel); box-shadow: var(--lift-2); }
body.koda-page-home .win:has(input:checked){
  background: linear-gradient(180deg, #fff, var(--steel-soft));
  border-color: var(--steel); box-shadow: var(--lift-2), inset 0 1px 0 #fff;
}
body.koda-page-home .win:has(input:checked) .win-name{ color: var(--steel-deep); }
body.koda-page-home .win:has(input:checked) small{ color: var(--steel-deep); }
/* Struck through and not clickable. The strike is what separates "gone" from
   "not chosen yet" at a glance — opacity alone reads as a style, not a fact. */
body.koda-page-home .win.gone{
  cursor: not-allowed; background: var(--page-deep); box-shadow: var(--press);
  border-color: var(--rule); border-style: dashed;
}
body.koda-page-home .win.gone:hover{ border-color: var(--rule); box-shadow: var(--press); }
body.koda-page-home .win.gone .win-name{
  color: var(--muted); text-decoration: line-through; text-decoration-thickness: 1.5px;
}
body.koda-page-home .win.gone small{ font-weight: 700; color: var(--muted); }
/* The panel, when the customer pressed Continue without picking one. */
body.koda-page-home #winPanel.bad .wins{
  border-radius: var(--radius); box-shadow: 0 0 0 4px rgba(191,150,52,.28);
}
body.koda-page-home .win:focus-within{ outline: 3px solid var(--steel); outline-offset: 2px; }
@media (max-width: 420px){ body.koda-page-home .wins{ grid-template-columns: 1fr; } }

/* ── STEP 4 ─────────────────────────────────────────────────────────────── */
/* Inside the card the fields sit tighter than they do in the sign-in drawer:
   this step is five rows and a note, the drawer is two. The shared .field
   rhythm is right for the drawer, so it is narrowed here rather than changed. */
body.koda-page-home .book-body .field{ margin-bottom: 13px; }
body.koda-page-home .book-body .field textarea{ min-height: 62px; }
/* City and ZIP: a five-digit box does not need half the row. */
body.koda-page-home .row2-zip{ grid-template-columns: 1.6fr 1fr; }
body.koda-page-home .field .opt{ font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--ink-soft); }
/* Flagged by the page's own validation. `input:invalid` cannot do this job —
   it fires on a half-typed email — so the class is added on submit and removed
   on the next keystroke. */
body.koda-page-home .field input.bad, body.koda-page-home .field textarea.bad{ border-color: #c8382c; }

/* ── The text-message consent box ──────────────────────────────────────────
   Fine print by function, NOT by size. It is the wording a carrier reads back
   to decide whether KODA is allowed to text at all, and a customer who cannot
   read what they are agreeing to has not agreed to it. So it sits at 14.5px —
   above the 14px floor CLAUDE.md §4.1 sets for secondary copy — and not at the
   11px a consent line usually gets.

   THE BOX IS 20px AND THE LABEL IS THE HIT AREA. A 13px native checkbox is a
   target people miss on a phone; wrapping the whole sentence in the <label>
   makes the target the paragraph. `align-items: start` keeps the box on the
   first line rather than floating to the middle of four. */
body.koda-page-home .sms-consent{
  display: grid; grid-template-columns: 20px 1fr; gap: 11px; align-items: start;
  margin: 18px 0 16px; padding: 14px 15px;
  border: 1px solid var(--rule-firm); border-radius: var(--radius);
  font-size: 14.5px; line-height: 1.5; color: var(--ink-soft); cursor: pointer;
}
body.koda-page-home .sms-consent input{
  width: 20px; height: 20px; margin: 1px 0 0; accent-color: var(--act); cursor: pointer;
}
/* --steel-deep, not --steel: this is 14.5px, and the theme's own note says
   small type takes the deeper slate or it misses AA on white. */
body.koda-page-home .sms-consent a{ color: var(--steel-deep); }
body.koda-page-home .sms-consent:focus-within{ outline: 3px solid var(--steel); outline-offset: 2px; }

/* ── The skeleton ──────────────────────────────────────────────────────────
   The shape of the form, before the form exists. It is deliberately inert: no
   button, no phone number, nothing a visitor could try to act on and have
   change under them. See the note in index.html. */
/* ═══ THE PLACEHOLDER RESERVES THE ROOM, IT DOES NOT MERELY SUGGEST IT ═════
   Jeremy, 2026-09-16: *"The hero section of the home page loads in smaller then
   expands when the intake form loads."*

   THE OLD SKELETON WAS 324px OF CONTENT WHERE STEP ONE IS 611. Two bars and a
   3 x 2 tile grid: the right idea, at less than half the size of the thing it
   was standing in for. The card came up 470px tall and became 797 the moment
   the catalog landed — Chrome logged it as a 0.045 shift on .book-body, 370 to
   657 — and because .hero is exactly as tall as the card, the whole hero grew
   under the reader. A placeholder that does not hold the space is a placeholder
   that has only moved the jank later.

   EVERY NUMBER BELOW IS THE REAL FORM'S. The field is a 26px label block, a 7px
   gap and a 60px input (93), thirteen apart. The heading block is 41, the hint
   36, the address field 103 (its label is a shorter block), the button 62 with
   18 above, the fine print 33. They total 611, which is what step one measures
   from 1440 down to 560.

   AND THE TWO ROWS ARE THE FORM'S OWN. `.row2` and `.row2-zip` are borrowed
   rather than re-declared, because .row2 collapses to one column at 520px and
   .row2-zip deliberately never does (koda-chrome.css / above). Borrowing them
   means this re-flows exactly where the form re-flows instead of guessing at
   it in a second media query. Only the two blocks that hold WRAPPING TEXT — the
   hint and the fine print, both of which take a second line on a phone — need a
   breakpoint of their own, and with those the placeholder measures 759 at 375,
   which is again step one to the pixel. */
/* `flow-root`, not `block`, and it is worth a line: the first child of each of
   these carries a top margin, and a block with no border or padding lets that
   margin COLLAPSE out through its top edge instead of counting. Measured with
   `block`, the placeholder came out 596 against step one's 611 — 5px lost at the
   top of the card and 5 more out of each of the two standalone fields. A
   flow-root contains its children's margins, which is the whole reason the
   arithmetic above lands. */
body.koda-page-home .bk-skel{ display: flow-root; }
body.koda-page-home .bk-skel-line,
body.koda-page-home .bk-skel-lbl,
body.koda-page-home .bk-skel-input,
body.koda-page-home .bk-skel-btn{
  display: block; border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--page-deep) 0%, #f7f9fb 40%, var(--page-deep) 80%);
  background-size: 300% 100%;
  animation: bkshimmer 1.5s ease-in-out infinite;
}
/* 7 + 24 + 14 = 45 and 0 + 14 + 18 = 32, which is the heading's 41 and the
   hint's 36 with the 4px that ADJACENT SIBLING MARGINS COLLAPSE moved across
   the join. Written the obvious way (10 under the heading, 4 over the hint) the
   pair collapses to max(10, 4) and the placeholder came out 607 against step
   one's 611 — the last 4px of a 327px jump, and the only kind of arithmetic
   error that survives a careful reading of the numbers. The hint's top margin is
   zero so there is nothing left to collapse with. */
body.koda-page-home .bk-skel-title{ height: 24px; width: 58%; margin: 7px 0 14px; }
body.koda-page-home .bk-skel-hint{ height: 14px; width: 82%; margin: 0 0 18px; }
/* The field: a label block of 26 (5 + 16 + 5), a 7px gap, a 60px input. 93, and
   13 to the next one — which is also what makes a .row2 grid row 106 tall. */
body.koda-page-home .bk-skel-field{ display: flow-root; margin-bottom: 13px; }
body.koda-page-home .bk-skel-lbl{ height: 16px; width: 46%; margin: 5px 0 12px; }
body.koda-page-home .bk-skel-input{ height: 60px; }
/* The address field's label is a single line rather than a label block: 90. */
body.koda-page-home .bk-skel-field-addr .bk-skel-lbl{ margin: 4px 0 10px; }
body.koda-page-home .bk-skel-btn{ height: 62px; margin-top: 18px; }
/* 16 + 17 = 33, the line under the button. Centred, because it is. */
body.koda-page-home .bk-skel-fine{ height: 17px; width: 62%; margin: 16px auto 0; }
@media (max-width: 520px){
  /* Both of these take a second line on a phone, and a bar cannot wrap. */
  body.koda-page-home .bk-skel-hint{ margin-bottom: 39px; }
  body.koda-page-home .bk-skel-fine{ height: 38px; }
}
@keyframes bkshimmer{ 0%{ background-position: 100% 0; } 100%{ background-position: 0 0; } }
@media (prefers-reduced-motion: reduce){
  body.koda-page-home .bk-skel-line,
  body.koda-page-home .bk-skel-lbl,
  body.koda-page-home .bk-skel-input,
  body.koda-page-home .bk-skel-btn{ animation: none; }
}
/* Announced, but not drawn: the skeleton is aria-hidden, so this carries the
   status for anyone listening rather than looking. */
body.koda-page-home .bk-skel-sr{
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ── The one price ──────────────────────────────────────────────────────────
   CLAUDE.md §6 plus Jeremy's carve-out: gather everything, then show ONE
   number, once, at the end. It gets the visual treatment of a result — a navy
   plate with a foil rule — because it is the answer the whole form was asking
   for. */
body.koda-page-home .quote-plate{
  position: relative; text-align: center; color: #fff;
  background: linear-gradient(178deg, var(--ink-lift), var(--ink-mid) 55%, var(--ink));
  border-radius: var(--radius); padding: 22px 22px 20px; margin-bottom: 18px;
  box-shadow: var(--lift-3);
  overflow: hidden;
}
body.koda-page-home .quote-plate::before{
  content: ''; position: absolute; inset: 0; background-image: var(--grain);
  opacity: .12; mix-blend-mode: overlay;
}
body.koda-page-home .quote-plate::after{
  content: ''; position: absolute; left: 22%; right: 22%; top: 0; height: 2px;
  background: linear-gradient(90deg, transparent, var(--gold-lit), transparent);
}
body.koda-page-home .quote-label{ position: relative; font-size: 13px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: rgba(240,216,148,.9); }
/* ── THE NUMBER WAS NAVY ON NAVY, AND HERE IS HOW ───────────────────────────
   Jeremy, 2026-09-10: *"the estimated dollars is black on blue and can't be
   seen. Please make the dollar amount gold so it stands out."*

   It was, and nothing in this block did it. `.quote-plate` sets `color: #fff`,
   so a <p> inside it should have inherited white — but the booking card lives
   INSIDE .hero, and this file carries

       .koda-page-home .hero p { color: var(--text); text-shadow: none; }

   to strip the theme's white-on-photo styling off every label in the wizard
   (see THE SMUDGE, above). A DIRECT rule always beats an inherited value
   however specific the ancestor's, so the one <p> on this plate that had never
   been given a colour of its own took #1e2530 — near-black, at 54px, on navy.
   .quote-label and .quote-note both state their own colour, which is exactly
   why they looked fine and the price did not.

   So it states one, and the one it states is gilt — which is where this number
   should have been all along. It is the answer the whole form was asking for,
   it sits on the one navy plate on the page, and gilt on navy is the pairing
   the review pennant and the experience seal already use. Nocturne's bar on
   gilt type is about SMALL text on a LIGHT ground; --gold-lit on --ink at 54px
   is 7.9:1.

   THE FOIL IS PROGRESSIVE, AND THE FALLBACK IS THE COLOUR. `background-clip:
   text` with a transparent fill is how the number catches a light source rather
   than sitting flat, but a browser that supports the fill and not the clip
   paints the price invisible — which is a worse bug than the one being fixed.
   So the solid colour is stated first and unconditionally, and the gradient is
   only ever added inside @supports. */
body.koda-page-home .quote-value{
  position: relative; font-family: var(--font-display); font-weight: 800;
  font-size: clamp(40px, 7vw, 54px); line-height: 1.1; margin: 3px 0 2px;
  font-variant-numeric: tabular-nums;
  color: var(--gold-lit);
  text-shadow: 0 1px 0 rgba(13,20,30,.55), 0 2px 10px rgba(0,0,0,.35);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)){
  body.koda-page-home .quote-value{
    background: linear-gradient(178deg,
      #f6e6bd 0%, #e2c67f 34%, #c9a44c 58%, #e7cf8e 78%, #c9a44c 100%);
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
    /* A shadow under transparent-filled text is drawn behind the GRADIENT, so
       the tight 1px is what gives the glyphs an edge on the plate; the wide one
       would only fog it. */
    text-shadow: 0 2px 12px rgba(0,0,0,.45);
  }
}
/* 15px, not 14.5. CLAUDE.md §4.1 puts the secondary floor at 14px and KODA's
   audience skews 35+; this line is what tells somebody the number includes the
   drive, which is the sentence most likely to stop a phone call. */
body.koda-page-home .quote-note{ position: relative; font-size: 15px; color: rgba(255,255,255,.88); line-height: 1.45; }
body.koda-page-home .quote-value.loading{ opacity: .35; }

body.koda-page-home .recap{ border: 1px solid var(--rule); border-radius: var(--radius-sm); background: var(--page); padding: 13px 15px; margin-bottom: 18px; }
body.koda-page-home .recap dl{ display: grid; grid-template-columns: auto 1fr; gap: 5px 16px; font-size: 15px; }
body.koda-page-home .recap dt{ color: var(--ink-soft); }
body.koda-page-home .recap dd{ color: var(--text); font-weight: 600; text-align: right; }
body.koda-page-home .recap button{
  background: none; border: 0; color: var(--steel-deep); font-size: 15px; font-weight: 600;
  text-decoration: underline; cursor: pointer; padding: 8px 0 0; display: block; margin-left: auto;
}

body.koda-page-home .nav-row{ display: flex; gap: 10px; margin-top: 18px; }
body.koda-page-home .nav-row .btn-primary{ flex: 1; }
body.koda-page-home .fineprint{ font-size: 14px; color: var(--ink-soft); text-align: center; margin-top: 12px; line-height: 1.5; }

/* THE TWO FUNCTIONAL COLOURS. Neither is a brand token and neither should
   become one: an error has to look like an error and a success like a success,
   in the colours every person on the internet already reads that way. Nocturne
   has no opinion about them because no other public page has a form that can
   fail. They appear here, in these four rules, and nowhere else. */
body.koda-page-home .alert{
  font-size: 16px; line-height: 1.5; border-radius: var(--radius-sm);
  padding: 13px 16px; margin-bottom: 16px; border-left: 4px solid;
}
body.koda-page-home .alert-bad{ background: #fdf0ee; border-color: #c8382c; color: #8f231a; }
body.koda-page-home .alert-good{ background: #edf6f1; border-color: #1f8a5b; color: #14603e; }
body.koda-page-home .alert[hidden]{ display: none; }

/* Booking confirmation. */
body.koda-page-home .done{ text-align: center; padding: 8px 0 4px; }
body.koda-page-home .done-seal{
  width: 74px; height: 74px; margin: 0 auto 16px; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(180deg, #2aa06c, #1f8a5b);
  box-shadow: 0 10px 24px -8px rgba(31,138,91,.6), inset 0 2px 0 rgba(255,255,255,.35);
  animation: seal .5s var(--ease) both;
}
@keyframes seal { from { transform: scale(.6); opacity: 0; } to { transform: scale(1); opacity: 1; } }
body.koda-page-home .done h3{ font-size: 26px; margin-bottom: 10px; }
body.koda-page-home .done p{ font-size: 17px; color: var(--ink-soft); margin-bottom: 14px; }
body.koda-page-home .done .done-back{ font-size: 16px; color: var(--steel-deep); }


/* ═══════════════════════════════════════════════════════════════════════════
   REVIEW MARQUEE — the last 30 days of five-star reviews, scrolling.
   Placed immediately under the hero because that is where the people who never
   scroll can still see it. Initials only (lib/review-initials.js).
   ═══════════════════════════════════════════════════════════════════════════ */
body.koda-page-home .strip{
  position: relative; padding: 26px 0 30px;
  background: linear-gradient(180deg, var(--page-deep), var(--page));
  border-block: 1px solid var(--rule-soft);
  box-shadow: inset 0 8px 16px -12px rgba(13,26,43,.2), inset 0 -8px 16px -12px rgba(13,26,43,.14);
}
body.koda-page-home .strip[hidden]{ display: none; }
body.koda-page-home .strip-head{
  display: flex; align-items: center; justify-content: center; gap: 12px;
  font-size: 15px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  color: var(--steel-deep); margin-bottom: 20px; text-align: center; flex-wrap: wrap;
}
body.koda-page-home .strip-head .rule{ width: 40px; height: 1px; background: var(--rule-firm); }

body.koda-page-home .marquee{ position: relative; overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent); mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent); }
body.koda-page-home .marquee-track{ display: flex; gap: 16px; width: max-content; animation: slide 72s linear infinite; }
body.koda-page-home .marquee:hover .marquee-track, body.koda-page-home .marquee:focus-within .marquee-track{ animation-play-state: paused; }
@keyframes slide { from { transform: translateX(0); } to { transform: translateX(calc(-50% - 8px)); } }
@media (prefers-reduced-motion: reduce) {
  /* No auto-scroll. The strip becomes a normal horizontally scrollable row —
     the content is still all reachable, which a paused marquee would not be. */
  .marquee{ overflow-x: auto; }
  body.koda-page-home .marquee-track{ animation: none; }
}

body.koda-page-home .rev{
  width: 330px; flex-shrink: 0; background: var(--paper);
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 18px 20px 16px; box-shadow: var(--lift-2);
  display: flex; flex-direction: column; gap: 11px;
}
body.koda-page-home .rev-stars{ color: var(--gold); font-size: 16px; letter-spacing: 2px; }
body.koda-page-home .rev-body{ font-size: 16px; line-height: 1.55; color: var(--text); }
body.koda-page-home .rev-who{ display: flex; align-items: center; gap: 10px; margin-top: auto; padding-top: 4px; }
body.koda-page-home .rev-init{
  width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0; display: grid; place-items: center;
  font-family: var(--font-display); font-size: 14px; font-weight: 700; color: var(--gold-ink);
  background: linear-gradient(160deg, var(--gold-lit), var(--gold) 55%, var(--gold-deep));
  box-shadow: 0 2px 6px rgba(138,100,32,.35), inset 0 1px 0 rgba(255,255,255,.6);
  letter-spacing: -.02em;
}
body.koda-page-home .rev-meta{ font-size: 14px; color: var(--muted); line-height: 1.3; }
body.koda-page-home .rev-meta b{ display: block; color: var(--ink); font-size: 15px; }

/* ═══════════════════════════════════════════════════════════════════════════
   SECTIONS
   ═══════════════════════════════════════════════════════════════════════════ */
section{ position: relative; }
body.koda-page-home .band{ padding: 78px 0; }
body.koda-page-home .band-page{ background: var(--page); }
body.koda-page-home .band-mist{ background: var(--page-deep); background-image: var(--weave); }

/* A BAND THAT CARRIES A PHOTOGRAPH.
   The picture is TEXTURE, not subject: a white veil at 78% sits over it so the
   content on top keeps full contrast and the room reads as depth behind the
   page. Same treatment Nocturne gives the services band on the other public
   pages (.koda-page-home .services-section::before), so the two match.
   The photo and its crop arrive as custom properties from the markup, which
   keeps the URL beside the alt text that describes it. */
body.koda-page-home .band-photo{ position: relative; isolation: isolate; }
body.koda-page-home .band-photo::before{
  content: ''; position: absolute; inset: 0; z-index: -2;
  background-image: var(--band-photo);
  background-size: cover;
  background-position: var(--band-photo-pos, center);
}
body.koda-page-home .band-photo::after{
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: rgba(255, 255, 255, var(--band-veil, .78));
}
/* THE ROOM BEHIND "WHY PEOPLE CALL US" (2026-09-10). Jeremy: "Please add a
   clean living room image on the background of the 'why people call us' page."
   Same treatment as the services band — the photograph is TEXTURE under a veil,
   never a picture the content has to survive — but the veil is heavier here,
   because that band carries five WHITE cards and this one carries a pill field
   on one surface. Five white plates on a visible photograph reads as clutter;
   at .88 the room is depth behind them and the cards still have an edge. */
body.koda-page-home .probs-band{ --band-veil: .85; }
/* THE FAQ'S VEIL CAME DOWN 8% (2026-09-11). Jeremy: *"'The questions we get
   most' section — reduce the photo overlay by ~8%."* .82 → .75: the room is
   that much more readable behind the cards, which is what was asked for and
   costs nothing, because every answer sits inside a white card with its own
   border — no type on this band ever touches the photograph. */
body.koda-page-home .faq-band{ --band-veil: .75; }
/* ── AND THEN IT STOPPED BEING A FLAT VEIL (2026-09-14) ─────────────────
   Jeremy: *"Can you have the overlay on the background photo fade from right to
   left so the left side past the questions box becomes fully visible? That green
   plant should pop."*

   HE IS ASKING FOR THE VEIL TO SPEND ITSELF WHERE IT IS NEEDED. A flat wash at
   .75 is the same everywhere, which means it is set for the hardest place on
   the band — under the cards — and then applied to the two margins either side
   of them, where there is nothing to protect and a photograph worth looking at.
   The questions sit in an 800px column down the middle of a 1240px wrap; past
   its left edge the veil is doing no work at all, and the biggest, greenest
   thing in the room is exactly there.

   SO IT IS A RAMP, LEFT TO RIGHT. Nothing at the left margin, climbing to the
   full .75 by the time it reaches the cards and holding it for the rest of the
   band. Every answer keeps the contrast it had — the ramp is complete at 38%,
   and the first card starts at about 30% of the viewport and sits on its own
   white plate regardless.

   ONLY ON WIDE SCREENS. Below 900px the cards run the full width of the band,
   so there is no margin to give back and the flat veil is the right one. */
@media (min-width: 900px){
  body.koda-page-home .faq-band.band-photo::after{
    background: linear-gradient(90deg,
      rgba(255,255,255,0)                   0%,
      rgba(255,255,255,0)                   9%,
      rgba(255,255,255,.22)                20%,
      rgba(255,255,255,.55)                29%,
      rgba(255,255,255,var(--band-veil))   38%,
      rgba(255,255,255,var(--band-veil))  100%);
  }
}

/* A PHOTOGRAPH UNDER A NIGHT BAND. The white veil is exactly wrong here — it
   would grey the navy out and take the white type with it. So on .band-night
   the veil INVERTS: a navy wash at 88% over the picture, which leaves the band
   the colour it already was with a room faintly readable inside it, and leaves
   white type at the contrast it was designed for. The gradient the band
   normally paints has to step aside for the photograph, so it is redeclared as
   a transparent-to-navy fall in the ::after instead. */
body.koda-page-home .band-night.band-photo{ background: var(--ink); }
body.koda-page-home .band-night.band-photo::after{
  /* WEIGHTED WHERE THE WORDS ARE. A flat 90% navy is safe and shows nothing —
     the first pass of this was a navy band with a photograph provably behind it
     and visibly not there. So the wash is a radial: heaviest under the centred
     headline and buttons, thinning to 58% at the corners, where the room can
     actually be seen and where no type ever lands. White on the middle stop
     measures better than 12:1; the light corners carry nothing but photograph.
     The linear on top is the band's own top-to-bottom fall, kept so the section
     still sits in the page the way the other night bands do. */
  background:
    radial-gradient(76% 128% at 50% 50%,
      rgba(22,32,46,.93) 0%, rgba(22,32,46,.88) 42%,
      rgba(22,32,46,.72) 74%, rgba(22,32,46,.58) 100%),
    linear-gradient(178deg, rgba(38,55,77,.34) 0%, rgba(29,43,61,.40) 45%, rgba(22,32,46,.58) 100%);
}
/* ONE ELEMENT HAS ONE ::before, AND TWO RULES WERE CLAIMING IT. `.band-photo`
   paints the photograph there; `.band-night` paints the grain there, at the
   same specificity and LATER in this file, so it wins — a night band with a
   photograph would have shown the grain and no photograph at all. Restated
   here at (0,3,2) so the photograph wins on the one element that is both, and
   the grain is simply not needed: a room under a navy wash is already texture.
   `background-image` and `mix-blend-mode` both have to be said, or the
   photograph inherits the grain's overlay blend and turns to fog. */
body.koda-page-home .band-night.band-photo::before{
  z-index: -2; opacity: 1; mix-blend-mode: normal;
  background-image: var(--band-photo);
  background-size: cover;
  background-position: var(--band-photo-pos, center);
}
body.koda-page-home .band-night{
  background: linear-gradient(178deg, var(--ink-lift), var(--ink-mid) 45%, var(--ink));
  color: #fff; position: relative; overflow: hidden;
}
body.koda-page-home .band-night::before{ content: ''; position: absolute; inset: 0; background-image: var(--grain); opacity: .1; mix-blend-mode: overlay; }
body.koda-page-home .band-night > *{ position: relative; z-index: 1; }
body.koda-page-home .band-night h2, body.koda-page-home .band-night h3{ color: #fff; }
/* GOLD ON THE NIGHT BANDS. Jeremy: "the original nocturne had gold with the
   text in a classy way. we want to keep that gold throughout." An earlier pass
   stripped it out to keep gold scarce for the flag; that left the trust section
   "entirely white and blue and gray", which is exactly the flat look this whole
   redesign is about. Gold comes back wherever it labels something on a dark
   ground, and stays off large fills and small text on white. */
body.koda-page-home .band-night .eyebrow{ color: var(--gold-lit); }
body.koda-page-home .band-night .eyebrow::before{ background: linear-gradient(90deg, var(--gold), transparent); }
@media (max-width: 640px) {
  body.koda-page-home .band{ padding: 56px 0; }
  body.koda-page-home .prob-photo{ height: 140px; margin: -30px -28px 0; }
}

body.koda-page-home .sec-head{ max-width: 720px; margin-bottom: 42px; }
body.koda-page-home .sec-head.mid{ margin-inline: auto; text-align: center; }
/* A gilt hairline under a centred section head: the small classy flourish
   Nocturne uses, and the cheapest way to stop a white band reading as empty. */
body.koda-page-home .band-night .sec-head.mid h2::after,
body.koda-page-home .sec-head.mid h2::after{
  content: ''; display: block; width: 54px; height: 2px; margin: 16px auto 0;
  background: linear-gradient(90deg, transparent, var(--gold), transparent);
}
body.koda-page-home .sec-head.mid .eyebrow::before{ display: none; }
body.koda-page-home .sec-head p{ font-size: 18px; color: var(--muted); margin-top: 12px; line-height: 1.6; }
body.koda-page-home .band-night .sec-head p{ color: rgba(255,255,255,.8); }

/* ── Problem cards: allergies · pets · odors ─────────────────────────────────
   These replace the flat "Allergy Relief / Kid-Safe / Mold Prevention" strip.
   Each is a raised plate with a pressed icon medallion, a rule, and a lift on
   hover. Three, because Jeremy named three things to hit: the allergies, the
   pets, the smells. */
/* FOUR CARDS, ONE ROW (2026-09-11). There used to be five, and five is a
   number no row of this width can hold — so the grid was six columns of two,
   which gave three across and then stranded the last two on a half-empty row
   underneath. Jeremy took Routine Cleanings out and asked for Stains & Spills
   "on the same row as the others", and with four cards the whole apparatus goes
   with it: four equal columns, two on a tablet, one on a phone. No spans, no
   nth-child offsets, nothing to re-derive the next time a card is added. */
body.koda-page-home .probs{
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px;
}
@media (max-width: 1060px){
  body.koda-page-home .probs{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 700px){
  body.koda-page-home .probs{ grid-template-columns: minmax(0, 1fr); }
}
body.koda-page-home .prob{
  position: relative; background: var(--paper); border-radius: var(--radius);
  padding: 30px 28px 28px; border: 1px solid var(--rule);
  box-shadow: var(--lift-2);
  transition: transform .28s var(--ease), box-shadow .28s var(--ease);
  overflow: hidden;
}
body.koda-page-home .prob::after{
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: linear-gradient(90deg, var(--ink), var(--steel));
  transform: scaleX(0); transform-origin: left; transition: transform .38s var(--ease);
}
body.koda-page-home .prob:hover{ transform: translateY(-6px); box-shadow: var(--lift-4); }
body.koda-page-home .prob:hover::after{ transform: scaleX(1); }
/* The card's photograph. Full-bleed to the card's edges — the card carries its
   own padding, so the image is pulled back out by exactly that much rather than
   sitting inset, which would read as a thumbnail stuck on top. */
body.koda-page-home .prob-photo{
  /* max-width:none is load-bearing: the base `img { max-width: 100% }` rule
     clamps this to the CONTENT box, which leaves the photo 56px short of the
     card's right edge and only looks like a mistake. */
  width: calc(100% + 56px); max-width: none;
  margin: -30px -28px 0; height: 172px;
  object-fit: cover; display: block;
}
/* The medallion straddles the photograph's lower edge — it reads as one object
   with the picture, and it costs ~50px less height than stacking the two. */
/* THE MEDALLION, STRUCK (2026-09-10). Jeremy: "Update the icon to stand out a
   bit more, the grey on white gets lost."

   He is describing three near-white surfaces in a row: the card is white, the
   medallion was a white-to-mist gradient, and the glyph inside it was a --steel
   stroke. Nothing in that stack has an edge, so the icon read as a watermark on
   a plate on a plate.

   INVERTED. The medallion is now NAVY — the page's own chrome colour — with a
   gilt ring and a gilt-lit glyph. It sits on a white card, half over a
   photograph, and it is the darkest thing in the card, which is what makes it
   the first thing the eye lands on. Gold on navy measures about 8:1; gold on
   white would have been 2.37:1 and is barred (Nocturne), which is exactly why
   the medallion had to go dark rather than the glyph going gold. */
body.koda-page-home .prob-ico{
  width: 62px; height: 62px; border-radius: 16px; display: grid; place-items: center;
  position: relative; z-index: 1; margin-top: -34px; margin-bottom: 16px;
  background: linear-gradient(158deg, var(--ink-lift) 0%, var(--ink-mid) 48%, var(--ink) 100%);
  border: 1px solid rgba(201,164,76,.55);
  color: var(--gold-lit);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.20),
              0 2px 4px rgba(13,26,43,.28),
              0 10px 20px -8px rgba(13,26,43,.5);
  transition: transform .28s var(--ease), box-shadow .28s var(--ease);
}
/* The whole card lifts on hover; the medallion lifts a little further, so the
   two do not move as one flat sheet. */
body.koda-page-home .prob:hover .prob-ico{
  transform: translateY(-3px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.28),
              0 3px 6px rgba(13,26,43,.3),
              0 16px 28px -10px rgba(13,26,43,.6);
}
/* ── THE TITLE IS THE NAMEPLATE (2026-09-11) ────────────────────────────────
   Jeremy: *"The complaint is that there is nothing on these tiles that draws
   the eye … Replace the tile names with fonts that are either different
   type/color/etc. to help draw the eye, or use design elements to draw the eye
   cleanly and professionally."*

   THE TILE HAD ONE DARK OBJECT AND IT WAS NOT THE WORDS. The navy medallion is
   the loudest thing in the card and the name sat under it as plain body-sized
   heading — so a reader scanning four cards read four photographs, four badges,
   and no titles. What a scanner needs from this section is the four nouns.

   SO THE NAME IS SET AS A PLATE: the display face two steps up (22px against
   the theme's ~18), tracked in rather than out, and a struck gilt rule directly
   beneath it. The rule is the whole device. It is gold, which is the only
   accent colour this page owns and the one the eye follows down a column of
   white cards; it is 46px, so it reads as an underscore on the name rather than
   as a divider across the card; and it REPLACES the grey hairline that used to
   sit above the bullets, so the tile gains an accent without gaining a line.
   Gold is never the type here and never could be — 2.37:1 on white — so it is
   the mark under the type, exactly as it is under every centred section head on
   this page. */
body.koda-page-home .prob h3{
  font-family: var(--font-display); font-weight: 700;
  font-size: 22px; line-height: 1.18; letter-spacing: -.015em;
  color: var(--ink); margin-bottom: 12px;
}
body.koda-page-home .prob h3::after{
  content: ''; display: block; width: 46px; height: 3px; margin-top: 11px;
  background: linear-gradient(90deg, var(--gold) 0%, var(--gold) 72%, rgba(201,164,76,0) 100%);
}
/* NO LEDE. Each card used to open with a sentence and then answer it in the
   first bullet; Jeremy: *"Scrap the sentences in each tile but keep the check
   bullet points."* The rule stays declared because the section can still be
   handed a paragraph by a future edit, and a stray <p> at 17.5px in --ink-soft
   is a better failure than one at whatever the theme happens to give it. */
body.koda-page-home .prob p{ font-size: 17.5px; color: var(--ink-soft); line-height: 1.5; }
body.koda-page-home .prob ul{ list-style: none; }
body.koda-page-home .prob li{ display: flex; gap: 10px; align-items: flex-start; font-size: 16px; color: var(--text); padding: 5px 0; }
/* The ticks were inheriting --text, so five cards of bullets were five columns
   of small black checks doing the work of punctuation. Steel makes them read as
   marks against a list rather than as more words. */
body.koda-page-home .prob li svg{ flex-shrink: 0; margin-top: 4px; color: var(--steel-deep); }

/* ── PET URINE ──────────────────────────────────────────────────────────────
   A statement on the left, four definitions on the right, and nothing else.

   IT IS DELIBERATELY NOT A CARD ROW. The band above it is four photographed
   tiles; a second row of tiles here would read as the same section continuing,
   which is the opposite of what a band exists to do. So this one is set as
   type: a claim, and the four things it is a claim about. The only object in it
   is the button.

   THE BAND IS SHORTER THAN ITS NEIGHBOURS ON PURPOSE — 58px of padding against
   78 — because Jeremy asked for "a small section" and a band that takes a full
   screen is not a note, it is a chapter. */
body.koda-page-home .urine-band{ padding: 58px 0; }
/* ── THREE COLUMNS, ONE LINE (2026-09-14) ───────────────────────────────────
   Jeremy: *"Can you find a way to have the dog photo be on the same line as
   everything else?"*

   The band was a claim on the left and a 2x2 definition grid on the right, with
   the photograph parked under the button because there was nowhere else for it.
   Folding the four definitions into a name-list plus one panel halves their
   width, and the half it releases is the photograph's column. So the band is a
   claim, a picker and a picture, all on one line, at the height it already was.

   `align-items: stretch` is what makes the last of those work: the photo column
   is whatever the tallest of the three turns out to be, and the picture fills
   it (see .urine-shot). Nothing is given a fixed height anywhere in here. */
body.koda-page-home .urine{
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 5.2fr) minmax(0, 3.8fr);
  grid-template-areas: 'say pick shot';
  gap: 38px; align-items: stretch;
}
body.koda-page-home .urine-say{ grid-area: say; align-self: start; }
body.koda-page-home .urine-pick{ grid-area: pick; }
body.koda-page-home .urine-shot{ grid-area: shot; }
/* Below the point where three columns stop being three columns, the photograph
   drops under the CLAIM rather than under everything — it keeps its own line,
   beside the picker, so the arrangement Jeremy asked for holds as long as there
   is any room at all for it to hold in. */
@media (max-width: 1160px){
  body.koda-page-home .urine{
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    grid-template-areas: 'say pick' 'shot pick';
    gap: 30px 36px;
  }
}
@media (max-width: 900px){
  body.koda-page-home .urine{
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'say' 'pick' 'shot';
    gap: 28px;
  }
}
body.koda-page-home .urine-say h2{
  font-size: clamp(28px, 3.2vw, 38px); line-height: 1.08; margin: 6px 0 14px;
}
body.koda-page-home .urine-say p{
  font-size: 18px; line-height: 1.6; color: var(--ink-soft); max-width: 38ch;
  margin-bottom: 24px;
}

/* ── THE PICKER ─────────────────────────────────────────────────────────────
   A column of four names and one panel beside them. The gilt rule that used to
   sit over every definition now sits over the panel and over nothing else, so
   the gold in this band is a single mark instead of four — and the ACTIVE name
   carries the only other piece of it, as a left edge that fills while the
   rotation runs. Gold marks a heading here, it never sets one; that is the same
   rule the tile names one band up are following.

   WITHOUT JAVASCRIPT THIS IS A DEFINITION LIST. `.urine-panels` is ordinary
   flow until home.js adds `.is-live`, so all four headings and all four
   paragraphs are simply there, stacked, which is also what a crawler reads. The
   stacking and the hiding only begin once something exists to switch them. */
body.koda-page-home .urine-pick{
  display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: 22px; align-items: stretch;
}
/* THE STRETCH ONLY MAKES SENSE IN THE THREE-COLUMN BAND. Below 1160 the picker
   spans BOTH rows of a two-row grid — the claim over the photograph — so its
   column is about 560px tall, and four names divided into that are four names
   a hundred pixels apart. Here they go back to their own height and the panel
   back to the top, which lines the whole picker up with the headline opposite
   it instead of floating in the middle of its own column. */
@media (max-width: 1160px){
  body.koda-page-home .urine-pick{ align-items: start; }
  body.koda-page-home .urine-tab{ flex: 0 0 auto; }
  body.koda-page-home .urine-pick.is-live .urine-panels{ align-content: start; }
}
@media (max-width: 620px){
  body.koda-page-home .urine-pick{ grid-template-columns: minmax(0, 1fr); gap: 18px; }
}

/* THE NAMES FILL THE BAND'S HEIGHT. Four names at their own text height came
   to 187px in a 308px band — the claim beside them is what sets that height —
   which left the column looking like it had stopped early. `flex: 1` on each
   name divides the height between them instead, and since every tab carries a
   left edge and they sit flush, the four of them read as one rail with a gold
   marker sliding down it. In one column (≤900) the picker is auto-height, the
   flex has nothing to divide, and they go back to their own size. */
body.koda-page-home .urine-tabs{ display: flex; flex-direction: column; }
body.koda-page-home .urine-tab{
  position: relative; text-align: left; cursor: pointer;
  flex: 1 1 auto; display: flex; align-items: center;
  background: none; border: 0; border-left: 3px solid var(--rule);
  padding: 12px 16px 12px 15px; width: 100%;
  font-family: var(--font-display); font-weight: 700; font-size: 19px;
  letter-spacing: -.012em; line-height: 1.2; color: var(--muted);
  white-space: nowrap;
  transition: color .2s var(--ease), background .2s var(--ease);
}
body.koda-page-home .urine-tab:hover{ color: var(--ink); background: rgba(30,37,48,.04); }
body.koda-page-home .urine-tab[aria-selected="true"]{
  color: var(--ink); background: rgba(201,164,76,.10);
}
/* THE EDGE IS THE CLOCK. At rest it is simply the gold marker on the chosen
   name. While the rotation is running it GROWS, from nothing to the full height
   of the name, over exactly the dwell — so the panel never changes without
   having said first that it was about to. */
body.koda-page-home .urine-sweep{
  position: absolute; left: -3px; top: 0; bottom: 0; width: 3px;
  background: var(--gold); transform: scaleY(0); transform-origin: 50% 0;
}
body.koda-page-home .urine-tab[aria-selected="true"] .urine-sweep{ transform: scaleY(1); }
body.koda-page-home .urine-tabs.is-auto .urine-tab[aria-selected="true"] .urine-sweep{
  animation: urine-sweep var(--urine-dwell, 6s) linear both;
}
body.koda-page-home .urine-tabs.is-auto.is-held .urine-tab[aria-selected="true"] .urine-sweep{
  animation-play-state: paused;
}
@keyframes urine-sweep{ from{ transform: scaleY(0); } to{ transform: scaleY(1); } }

body.koda-page-home .urine-panel{ padding-top: 14px; border-top: 2px solid var(--gold); }
body.koda-page-home .urine-panel + .urine-panel{ margin-top: 22px; }
body.koda-page-home .urine-panel h3{
  font-family: var(--font-display); font-weight: 700; font-size: 20px;
  color: var(--ink); letter-spacing: -.012em; margin-bottom: 6px;
}
body.koda-page-home .urine-panel p{ font-size: 18px; line-height: 1.6; color: var(--text); }

/* ── THE ONE PANEL THAT CARRIES A LABEL ABOVE THE RULE (2026-09-16) ─────────
   Jeremy: *"when it is clicked, above the gold line put 'Contamination
   Service'."*

   THE RULE IS THE PANEL'S OWN border-top, so nothing can be put above it from
   inside the bordered box — the border IS the box's top edge. So this one panel
   hands the rule down to a wrapper: `.urine-panel-lead` gives up its border and
   its padding, `.urine-panel-body` takes both, and the kicker sits in the gap
   that leaves. Every other panel in the list is untouched, which is the point
   of doing it with a class rather than with a pseudo-element and a negative
   margin.

   AND IT IS GILT AT 15px, NOT GOLD BODY COPY. It is a label for the panel
   under it, so it is set the way every other eyebrow on this page is set —
   small, tracked out, capitals — and it takes the gold DEEP rather than the
   gold the rule is drawn in, because small gold type on a near-white ground at
   the rule's own value is the one place gilt stops being readable. CLAUDE.md
   §4.1 floor for a secondary label on the web is 14px; this is 15. */
body.koda-page-home .urine-panel-lead{ padding-top: 0; border-top: 0; }
body.koda-page-home .urine-panel-lead .urine-panel-body{
  padding-top: 14px; border-top: 2px solid var(--gold);
}
body.koda-page-home .urine-panel p.urine-kicker{
  font-family: var(--font-body); font-size: 15px; font-weight: 700;
  line-height: 1.2; letter-spacing: .1em; text-transform: uppercase;
  color: var(--gold-deep); margin: 0 0 9px;
}

/* ── AND NOW THE LIVE VERSION ───────────────────────────────────────────────
   All five panels are stacked into ONE grid cell, so the box is as tall as the
   longest of them and never resizes as they take turns — a panel that grew and
   shrank on a six-second clock would make the whole band breathe. The inactive
   four are `visibility: hidden` rather than `display: none` on purpose: they
   have to keep holding the cell open, and `visibility` is the one that takes
   them out of the accessibility tree while they do it. (`hidden` is not an
   option — [hidden] is `display: none !important` page-wide, by design.) */
/* The panel is three or four lines and the column beside it is the height of
   the whole band, so top-aligning it left a hole under the paragraph the size
   of the paragraph. Centred, the gilt rule and its sentence sit opposite the
   middle of the name rail and the column has no hole in it. The 28px of
   padding-top added in 2026-09-16 is the room the lead panel's kicker hangs
   in — see THE KICKER LEAVES THE FLOW below. */
body.koda-page-home .urine-pick.is-live .urine-panels{ display: grid; align-content: center; }
body.koda-page-home .urine-pick.is-live .urine-panel{
  grid-area: 1 / 1; margin-top: 0;
  opacity: 0; visibility: hidden; transform: translateY(5px);
  /* VISIBILITY IS IN THE TRANSITION, AND THAT IS NOT DECORATION. It is not an
     animatable property — it flips, it does not ramp — so without a delay the
     OUTGOING panel vanishes on the first frame while the incoming one is still
     at zero opacity, and the swap shows a blank box for a third of a second.
     Delaying it by the length of the fade on the way out, and not at all on the
     way in, is what makes the two panels actually cross. */
  transition: opacity .3s var(--ease), transform .3s var(--ease),
              visibility 0s linear .3s;
}
body.koda-page-home .urine-pick.is-live .urine-panel.is-on{
  opacity: 1; visibility: visible; transform: none;
  transition: opacity .3s var(--ease), transform .3s var(--ease),
              visibility 0s linear 0s;
}
/* The tab already says the name; repeating it two inches away is a stutter. */
body.koda-page-home .urine-pick.is-live .urine-panel h3{ display: none; }

/* ── AND THE KICKER LEAVES THE FLOW ONCE THE PANELS ARE STACKED ─────────────
   All five panels share one grid cell, so a kicker sitting IN the flow of one
   of them would push that panel's gilt rule 24px below where the other four
   draw theirs — and the rule is the one fixed line in the box. Every six
   seconds it would drop and come back, which reads as a fault rather than as a
   label.

   So in the live picker the kicker is lifted out of the flow and hung above the
   panel's own top edge, which is exactly where the rule is. The rule then sits
   in the same place for all five, and only the words above it come and go.
   `.urine-panels` reserves the height it needs, so nothing it is hung over can
   ever be a paragraph.

   NOT in the no-JavaScript fallback, where the panels are a stacked definition
   list and each rule is the top of its own item — there the kicker is simply
   the first line of the last item, in the flow, which is correct. */
body.koda-page-home .urine-pick.is-live .urine-panels{ padding-top: 28px; }
body.koda-page-home .urine-pick.is-live .urine-panel-lead{ position: relative; }
body.koda-page-home .urine-pick.is-live .urine-panel-lead .urine-kicker{
  position: absolute; left: 0; right: 0; bottom: 100%; margin: 0 0 9px;
}

/* ── THE PHOTOGRAPH ─────────────────────────────────────────────────────────
   Absolutely positioned inside its own box, which is the one arrangement that
   survives all three layouts: in the three-column band the box is stretched to
   the row's height by the grid, in the two-column one it is `min-height`, and
   the picture fills whichever of those it is given without ever being asked to
   resolve `height: 100%` against an `auto` row.

   `object-position: center 58%` is CLAUDE.md §9.2 applied literally: the subject
   of this picture is a dog ON CARPET, so the crop is pinned low enough to hold
   both. Centred, it keeps the wall. */
body.koda-page-home .urine-shot{
  position: relative; min-height: 260px; margin: 0;
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--lift-2);
}
body.koda-page-home .urine-photo{
  position: absolute; inset: 0; display: block;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center 58%;
}
@media (max-width: 900px){
  body.koda-page-home .urine-shot{ min-height: 0; aspect-ratio: 3 / 2; }
}

/* ── EVERYTHING WE CLEAN: two rails ────────────────────────────────────────
   Replaces the field of pills and the shared accordion panel beneath it.

   Jeremy: *"Remove the white box. Darken the pill outline then add some color
   in the pills. The white pills on white overlay feels off. Perhaps instead of
   having the pills clickable we have the services scrolling like we do with the
   Google reviews… Keep the description focused on what the customer gets out of
   the service."*

   THE WHITE BOX IS GONE, AND SO IS THE PROBLEM UNDER IT. The pills sat on a
   white .svc-shell card, which sat on a photograph under a white veil: three
   white surfaces stacked, and a 1.5px rgba(30,37,48,.14) outline trying to
   separate the top two. That outline is 14% of near-black — it is a hairline
   for a DIVIDER, not an edge for an object, and no amount of darkening it would
   have fixed a white pill on a white card on a white veil. So the card went and
   the cards themselves became the objects: each sits directly on the veiled
   room, with a real border, a real shadow, and a navy-and-gilt medallion that
   gives every one of them a dark anchor point.

   THE MOTION IS THE REVIEW STRIP'S — same duplicate-track trick, same edge
   mask. ONE rail, one direction: see ONE RAIL, ONE DIRECTION in index.html for
   why the two opposed rails went. */
/* ── THE FOOT IS TWO ROWS NOW, AND THE BAND PAID FOR IT (2026-09-16) ────────
   Jeremy: *"Add a service dropdown under them all under the pause button. (keep
   that section the same height. there should be room.)"*

   HE WAS RIGHT THAT THERE WAS ROOM, AND IT WAS IN THE BOTTOM PADDING. The
   section measured 809px at 1440 with a 44px foot row and 78px of padding under
   it. The foot is now two rows — 40px of controls, a 12px gutter, a 47px select
   — so it costs 60px, and the padding gives back 60. The band measures 809px,
   the height it was, and the 18px left under the select is not the whole gap to
   the next section: #area opens with 78px of its own. */
body.koda-page-home .svc-band{ overflow: hidden; padding-bottom: 18px; }
/* ── THE ARROWS, AND THE MANUAL MODE THEY TURN ON ───────────────────────────
   Jeremy, 2026-09-11: *"'If you sit on it or stand on it' section — add arrows
   to the scroll so customers can find the service they want."*

   A BELT IS NOT A LIST YOU CAN LOOK THINGS UP IN. Seventeen services go past at
   about 34px a second and the only control was Pause, which freezes whatever
   happens to be on screen at that instant. Somebody hunting for "tile and
   grout" had to wait for it to come round.

   SO AN ARROW HANDS THE RAIL OVER. The first press switches it out of marquee
   and into an ordinary horizontal scroller — the same shape the
   reduced-motion path has always used, so this is a mode the rail already knew
   how to be — and every press after that steps it one card. Position is
   carried across at the moment of the switch (see wireRail in home.js), so
   nothing jumps under the finger.

   The arrows are SIBLINGS of the rail, not children, because .svcrail carries
   an edge mask: a button inside it would be faded out by the very gradient that
   softens the belt's ends. .svcrail-frame is what they are positioned against.
   They also sit outside the `:has(.svcard:hover)` pause rule by construction —
   an arrow is not a card, so reaching for one does not freeze the thing you are
   trying to move. */
body.koda-page-home .svcrail-frame{ position: relative; }
body.koda-page-home .svcrail-arrow{
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 4;
  width: 46px; height: 46px; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center;
  background: var(--paper); color: var(--ink);
  border: 1.5px solid var(--rule-firm);
  box-shadow: var(--lift-3), inset 0 1px 0 #fff;
  transition: transform .16s var(--ease), box-shadow .16s, border-color .16s, background .16s;
}
body.koda-page-home .svcrail-arrow.prev{ left: 14px; }
body.koda-page-home .svcrail-arrow.next{ right: 14px; }
body.koda-page-home .svcrail-arrow:hover{
  border-color: var(--ink); box-shadow: var(--lift-4); background: #fff;
}
body.koda-page-home .svcrail-arrow.prev:hover{ transform: translateY(-50%) translateX(-2px); }
body.koda-page-home .svcrail-arrow.next:hover{ transform: translateY(-50%) translateX(2px); }
body.koda-page-home .svcrail-arrow:active{ box-shadow: var(--press); }
body.koda-page-home .svcrail-arrow:focus-visible{
  outline: none; border-color: var(--steel); box-shadow: 0 0 0 3px rgba(96,120,152,.32), var(--lift-3);
}
@media (max-width: 640px){
  /* On a phone the rail is a thumb-swipe away from being a scroller anyway, and
     46px of button over a 300px card covers the thing it is meant to reveal. */
  body.koda-page-home .svcrail-arrow{ width: 40px; height: 40px; }
  body.koda-page-home .svcrail-arrow.prev{ left: 6px; }
  body.koda-page-home .svcrail-arrow.next{ right: 6px; }
}
/* MANUAL MODE. The marquee stops for good and the rail becomes what it looks
   like: a horizontal scroller. Snap so a press always lands a card square
   rather than half of one, and the scrollbar goes because the arrows are the
   control now. */
body.koda-page-home .svcrail.is-manual{
  overflow-x: auto; scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  scrollbar-width: none; -ms-overflow-style: none;
}
body.koda-page-home .svcrail.is-manual::-webkit-scrollbar{ display: none; }
body.koda-page-home .svcrail.is-manual .svcrail-track{ animation: none !important; }
body.koda-page-home .svcrail.is-manual .svcard{ scroll-snap-align: center; }
@media (prefers-reduced-motion: reduce){
  body.koda-page-home .svcrail.is-manual{ scroll-behavior: auto; }
}

body.koda-page-home .svcrail{
  position: relative;
  /* The rails run edge to edge, not inside .wrap — a marquee that starts and
     stops at a content margin looks like a widget; one that runs off both sides
     of the screen looks like a belt. */
  margin-bottom: 18px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
  overflow: hidden;
}
body.koda-page-home .svcrail-track{
  display: flex; gap: 18px; width: max-content;
  padding: 6px 9px;                 /* room for the cards' own shadows */
  /* PACED, NOT TIMED. Seventeen cards at 324px each is a 5,508px half-track;
     at the two-rail speed (2,916px in 86s, about 34px a second) that is 162
     seconds a lap. 150s is a shade brisker than the pair it replaces, which is
     the right direction for a belt that is now twice as long. */
  animation: svcSlide 150s linear infinite;
}

/* ── WHAT STOPS IT, AND WHAT NO LONGER DOES ─────────────────────────────────
   Jeremy: *"any time the mouse is anywhere near the window it freezes … we
   can't have them stop any time the mouse is on the screen as that's not
   reasonable."*

   It was `.svc-band:hover` — the whole section. Full-bleed, about 700px tall,
   sitting in the middle of a long page: scrolling past with the cursor anywhere
   near the middle of the screen froze the rail without the visitor going near a
   card, and left it frozen. That is not a pause, it is a tripwire.

   A PAUSE HAS TO BE ABOUT A CARD. `:has(.svcard:hover)` stops the belt when the
   pointer is actually ON one of the things it carries — which is the only
   moment a reader has told us anything. Crossing the strip's margins, the
   headline above it or the line below it now does nothing at all. `:has` is
   baseline in every browser this page supports; a browser without it simply
   never auto-pauses, and the button below still works, which is the correct way
   round for a progressive enhancement on a decorative belt.

   `:focus-within` is the keyboard's version of the same sentence: tabbing into
   a card is stopping for it. */
body.koda-page-home .svcrail:has(.svcard:hover) .svcrail-track,
body.koda-page-home .svcrail:focus-within .svcrail-track,
body.koda-page-home .svcrail.is-paused .svcrail-track{ animation-play-state: paused; }
@keyframes svcSlide { from { transform: translateX(0); } to { transform: translateX(calc(-50% - 9px)); } }

@media (prefers-reduced-motion: reduce){
  /* No auto-scroll: the rail becomes an ordinary horizontal scroller, so all
     seventeen services are still reachable. A merely PAUSED marquee would hide
     most of the catalogue from the people who asked for less motion. */
  body.koda-page-home .svcrail{ overflow-x: auto; }
  body.koda-page-home .svcrail-track{ animation: none; }
  /* … UNLESS THE VISITOR ASKED FOR IT OUT LOUD. Jeremy: "the scrolling didn't
     work for me a few times." On a machine with Windows animations switched off
     that was this rule doing its job, and the job is right — but a person who
     has just pressed Start has told us more about what they want right now than
     an OS setting did, and honouring the preference over the press would be
     obeying the wrong human. The rail ships stopped for them either way. */
  body.koda-page-home .svcrail.is-playing{ overflow-x: hidden; }
  body.koda-page-home .svcrail.is-playing .svcrail-track{
    animation: svcSlide 150s linear infinite;
  }
}

/* ── THE CONTROL ────────────────────────────────────────────────────────────
   Outside the rail on purpose: a button INSIDE it would be a hoverable thing
   that is not a card, which is the exact ambiguity the rule above exists to
   remove. It sits with the footer line because that is where somebody who has
   just watched a card slide away from them is already looking. */
body.koda-page-home .svcrail-foot-wrap{
  display: flex; align-items: center; justify-content: center;
  column-gap: 16px; row-gap: 12px; flex-wrap: wrap;
}
/* ── THE JUMP ───────────────────────────────────────────────────────────────
   `flex: 0 0 100%` is what puts it UNDER the pause button rather than beside
   it: a flex item that asks for the whole line gets its own line, which is a
   break declared by the item that needs one rather than by a spacer element
   that means nothing. */
body.koda-page-home .svcjump{
  flex: 0 0 100%;
  display: flex; align-items: center; justify-content: center;
  gap: 10px; flex-wrap: wrap;
}
body.koda-page-home .svcjump-lbl{
  /* 16px, not 15: this is a sentence, not a button label, and §4.1's body floor
     is what a sentence answers to. */
  font-size: 16px; font-weight: 600; color: var(--ink-soft);
}
/* THE ARROW IS DRAWN, NOT LEFT TO THE PLATFORM. `appearance: none` is what lets
   this match the Pause button beside it — same paper, same firm rule, same
   pill, same lift — and the chevron is a two-gradient mark in the background so
   the control still carries no image.

   16px IS ALSO A MOBILE SAFARI RULE. A <select> under 16px makes iOS zoom the
   whole page on focus, and it does not zoom back out. */
body.koda-page-home .svcjump-sel{
  appearance: none; -webkit-appearance: none;
  font-family: var(--font-body); font-size: 16px; font-weight: 600;
  color: var(--ink); background-color: var(--paper);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink) 50%),
    linear-gradient(135deg, var(--ink) 50%, transparent 50%);
  background-position: right 19px center, right 13px center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  border: 1.5px solid var(--rule-firm); border-radius: 999px;
  padding: 9px 40px 9px 18px; min-height: 44px; cursor: pointer;
  box-shadow: var(--lift-1), inset 0 1px 0 #fff;
  transition: border-color .16s, box-shadow .16s;
  max-width: 100%;
}
body.koda-page-home .svcjump-sel:hover{ border-color: var(--ink); box-shadow: var(--lift-2); }
body.koda-page-home .svcjump-sel:focus-visible{
  outline: none; border-color: var(--steel); box-shadow: 0 0 0 3px rgba(96,120,152,.28);
}
/* The drop-down list itself is drawn by the OS, and on a dark system theme it
   inherits white-on-white unless the options are told what they are. */
body.koda-page-home .svcjump-sel option{ color: var(--ink); background: #fff; }
body.koda-page-home .svcrail-btn{
  display: inline-flex; align-items: center; gap: 8px; flex-shrink: 0;
  /* 15px is §4.1's floor for a button label, and that is what this is. */
  font-family: var(--font-body); font-size: 15px; font-weight: 700;
  color: var(--ink); background: var(--paper);
  border: 1.5px solid var(--rule-firm); border-radius: 999px;
  padding: 8px 16px 8px 13px; min-height: 40px; cursor: pointer;
  box-shadow: var(--lift-1), inset 0 1px 0 #fff;
  transition: border-color .16s, box-shadow .16s, background .16s;
}
body.koda-page-home .svcrail-btn:hover{ border-color: var(--ink); box-shadow: var(--lift-2); }
body.koda-page-home .svcrail-btn:focus-visible{
  outline: none; border-color: var(--steel); box-shadow: 0 0 0 3px rgba(96,120,152,.28);
}
/* Two bars or a triangle, drawn in borders so the control carries no image and
   nothing to load. The state is on aria-pressed; this follows it. */
body.koda-page-home .svcrail-btn-ico{
  width: 13px; height: 13px; flex-shrink: 0;
  border-left: 4px solid var(--ink); border-right: 4px solid var(--ink);
}
body.koda-page-home .svcrail-btn[aria-pressed="true"] .svcrail-btn-ico{
  width: 0; height: 0;
  border-left: 11px solid var(--ink); border-right: 0;
  border-top: 7px solid transparent; border-bottom: 7px solid transparent;
}

body.koda-page-home .svcard{
  width: 306px; flex-shrink: 0; display: flex; flex-direction: column;
  background: linear-gradient(180deg, #fff 0%, var(--page-deep) 100%);
  /* THE DARKER EDGE Jeremy asked for. --rule-firm is .26 of near-black against
     the old .14 — an edge rather than a divider — and the top rule is the
     "colour in the pills": a steel-to-gilt bar that is the only thing on the
     card doing anything decorative. */
  border: 1px solid var(--rule-firm);
  border-radius: var(--radius);
  box-shadow: var(--lift-2);
  padding: 22px 22px 18px;
  position: relative; overflow: hidden;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), border-color .2s;
}
body.koda-page-home .svcard::before{
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: linear-gradient(90deg, var(--ink) 0%, var(--steel) 55%, var(--gold) 100%);
}
body.koda-page-home a.svcard:hover{
  transform: translateY(-4px); box-shadow: var(--lift-4); border-color: var(--ink);
}
/* Flood & mold is the one that is not a booking, and it is allowed to say so
   in colour. A left edge, not red text — CLAUDE.md 3.1 rule 2 is a portal rule,
   but the reasoning ("colour is carried by fills and edges, never by the
   words") is just as true out here. */
body.koda-page-home .svcard-urgent::before{
  background: linear-gradient(90deg, #8b1f18 0%, #c8382c 60%, var(--gold) 100%);
}

/* The same struck medallion as the problem cards, at tile size. It is the
   darkest thing on the card, which is what a white card standing on a veiled
   photograph needs if it is going to have a focal point at all. */
body.koda-page-home .svcard-ico{
  width: 44px; height: 44px; border-radius: 12px; flex-shrink: 0;
  display: grid; place-items: center; margin-bottom: 13px;
  background: linear-gradient(158deg, var(--ink-lift) 0%, var(--ink-mid) 48%, var(--ink) 100%);
  border: 1px solid rgba(201,164,76,.55);
  color: var(--gold-lit);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.20), 0 6px 14px -7px rgba(13,26,43,.55);
}
/* UNDERLINED, BECAUSE THE CARD IS A DOOR (2026-09-16). Jeremy: *"Underline the
   service names."* Seventeen cards go past with a heading, a line, two ticks
   and a "See what's involved" at the foot, and the only thing saying the whole
   tile is clickable was that last line — 15px, at the bottom, in steel. An
   underlined name is the oldest signal on the web for "this opens something",
   and it is on the one word somebody is actually scanning for.

   OFFSET AND THICKNESS ARE DECLARED, not left to the font. At 21px display
   weight the browser default rides into the descenders of a 'y' or a 'p'
   (Upholstery has both); 4px of offset clears them, and 2px of stroke is what
   keeps the rule visible at the 19px phone size. */
body.koda-page-home .svcard h3{
  font-size: 21px; margin-bottom: 5px;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  text-decoration-color: var(--gold);
}
body.koda-page-home a.svcard:hover h3{ text-decoration-color: var(--ink); }
body.koda-page-home .svcard-lede{
  font-size: 16px; line-height: 1.45; color: var(--ink-soft); margin-bottom: 13px;
}
body.koda-page-home .svcard ul{
  list-style: none; display: grid; gap: 7px;
  padding-top: 12px; border-top: 1px solid var(--rule-soft);
}
body.koda-page-home .svcard li{
  display: flex; gap: 9px; align-items: flex-start;
  /* CLAUDE.md 4.1: 16px is the web BODY floor, and these bullets are the card's
     content rather than a caption on it. The panel these cards replaced set its
     bullets at 16px for the same reason. */
  font-size: 16px; line-height: 1.45; color: var(--text);
}
body.koda-page-home .svcard li svg{ flex-shrink: 0; margin-top: 3px; color: var(--steel-deep); }
body.koda-page-home .svcard-go{
  display: inline-flex; align-items: center; gap: 6px; margin-top: auto; padding-top: 14px;
  /* 15px is 4.1's floor for a navigation or button label, and that is what this
     is — the card is a link and this line is what says so. */
  font-size: 15px; font-weight: 700; letter-spacing: .01em; color: var(--steel-deep);
}
body.koda-page-home a.svcard:hover .svcard-go{ color: var(--ink); }
body.koda-page-home .svcard-go svg{ transition: transform .2s var(--ease); }
body.koda-page-home a.svcard:hover .svcard-go svg{ transform: translateX(3px); }
/* The three services with no page of their own say what to do instead, rather
   than pretending to be a link that goes nowhere. */
body.koda-page-home .svcard-go-call{ color: var(--act); }

body.koda-page-home .svcrail-foot{
  text-align: center; font-size: 17px; color: var(--ink-soft); margin-top: 0;
}
body.koda-page-home .svcrail-foot a{ color: var(--steel-deep); font-weight: 700; text-decoration: underline; }

@media (max-width: 640px){
  body.koda-page-home .svcard{ width: 268px; padding: 18px 18px 15px; }
  body.koda-page-home .svcard h3{ font-size: 19px; }
}

/* ═══ THE SERVICE SHEET ══════════════════════════════════════════
   The panel a service card opens instead of a page. See the long note in
   index.html for why it exists; this is how it is built.

   TWO COLUMNS, AND THE PHOTOGRAPH IS THE SECOND ONE. The reference Jeremy sent
   is the Rug Types block from /rug-cleaning: type on the left, a single
   photograph on the right, floating on the page under a deep soft shadow.
   That layout is doing something specific — the picture is not decoration
   beside the text, it is the ANSWER to the text, and putting it in its own
   column at full height is what makes the two read as one statement.

   THE DEPTH IS THE PAGE'S OWN. --lift-3 under the frame, the same shadow the
   booking card carries, so the photograph sits on the sheet the way the card
   sits on the hero. Nothing bespoke: a new shadow here would be a fourth
   depth on a page that has three.

   FIVE TICKS IN TWO COLUMNS, exactly like the reference. A five-item list in
   one column is a paragraph wearing bullets; in two it is a SPEC, which is
   what somebody deciding whether to call actually wants to read.
   ══════════════════════════════════════════════════════════════════════ */
body.koda-page-home .svcsheet-scrim{
  position: fixed; inset: 0; z-index: 900; background: rgba(22,32,46,.58);
  backdrop-filter: blur(4px);
  opacity: 0; transition: opacity .28s var(--ease);
}
body.koda-page-home .svcsheet-scrim.on{ opacity: 1; }

body.koda-page-home .svcsheet{
  position: fixed; inset: 0; z-index: 905;
  display: grid; place-items: center;
  padding: 24px; overflow-y: auto;
}
body.koda-page-home .svcsheet-card{
  position: relative; width: min(980px, 100%);
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 370px);
  gap: 40px; align-items: center;
  padding: 40px 40px 38px;
  background: #fff;
  border-radius: var(--radius);
  /* The gilt edge along the top is the booking card's; it is what says this
     object belongs to KODA rather than to the browser. */
  border-top: 3px solid var(--gold);
  box-shadow: 0 4px 10px rgba(16,24,36,.10), 0 40px 90px -24px rgba(16,24,36,.42);
  opacity: 0; transform: translateY(14px) scale(.985);
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
body.koda-page-home .svcsheet.on .svcsheet-card{ opacity: 1; transform: none; }

body.koda-page-home .svcsheet-x{
  position: absolute; top: 12px; right: 12px; z-index: 2;
  width: 38px; height: 38px; display: grid; place-items: center;
  background: #fff; color: var(--ink);
  border: 1px solid var(--rule); border-radius: 50%;
  cursor: pointer; transition: background .2s var(--ease), color .2s var(--ease);
}
body.koda-page-home .svcsheet-x:hover{ background: var(--ink); color: #fff; border-color: var(--ink); }
body.koda-page-home .svcsheet-x:focus-visible{ outline: 3px solid var(--steel); outline-offset: 2px; }

body.koda-page-home .svcsheet-say h2{
  font-size: clamp(28px, 3vw, 36px); line-height: 1.1; margin: 8px 0 14px;
  color: var(--ink); letter-spacing: -.015em;
}
/* TWO SENTENCES. That is the brief and it is also the right length: it is what
   fits above a five-item list without the list becoming the second thing you
   read rather than the point. */
body.koda-page-home .svcsheet-lede{
  font-size: 17px; line-height: 1.62; color: var(--ink-soft);
  max-width: 46ch; margin-bottom: 22px;
}
body.koda-page-home .svcsheet-list{
  list-style: none;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 11px 26px; margin-bottom: 26px;
}
@media (max-width: 620px){
  body.koda-page-home .svcsheet-list{ grid-template-columns: minmax(0, 1fr); }
}
body.koda-page-home .svcsheet-list li{
  position: relative; padding-left: 20px;
  /* 16px, which is CLAUDE.md §4.1's floor for body copy on the web and not the
     14px floor for a caption. These five lines are the substance of the sheet,
     not a note under it, and the audience this page is written for is 35+. */
  font-size: 16px; line-height: 1.5; color: var(--text);
}
/* A LOZENGE, NOT A DISC. The reference marks each line with a small rotated
   square in the brand burgundy — a diamond reads as a mark somebody chose and
   a dot reads as the browser's default, and at this size that difference is
   most of what makes a list look designed. */
body.koda-page-home .svcsheet-list li::before{
  content: ''; position: absolute; left: 2px; top: .55em;
  width: 7px; height: 7px; transform: rotate(45deg);
  background: var(--act);
}
body.koda-page-home .svcsheet-act{ display: flex; flex-wrap: wrap; gap: 12px; }
body.koda-page-home .svcsheet-more{ margin-top: 16px; font-size: 14px; }
body.koda-page-home .svcsheet-more a{
  color: var(--steel-deep); font-weight: 600; text-decoration: underline;
  text-underline-offset: 3px; text-decoration-thickness: 1px;
}
body.koda-page-home .svcsheet-more a:hover{ color: var(--ink); }

body.koda-page-home .svcsheet-shot{ margin: 0; }
body.koda-page-home .svcsheet-shot img{
  display: block; width: 100%; aspect-ratio: 4 / 5;
  object-fit: cover; object-position: var(--shot-pos, center center);
  border-radius: var(--radius);
  box-shadow: var(--lift-3);
}
/* ── AND ONE OF THEM IS A FILM ────────────────────────────
   Area Rugs shows a video where the other sixteen show a photograph, so this
   slot has to sit in the same hole the <img> does — same corner, same lift —
   at the one aspect a video is allowed to have. NOT the photograph's 4:5: a
   16:9 film letterboxed into a portrait frame is two black bars and a stamp,
   and there is no crop of a moving picture that fixes it. The column is
   narrower than the photo would have been tall, which is the trade. */
body.koda-page-home .svcsheet-vid{
  width: 100%; aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--radius); overflow: hidden;
  box-shadow: var(--lift-3);
}
body.koda-page-home .svcsheet-vid iframe{ display: block; width: 100%; height: 100%; border: 0; }

/* Stacked, the photograph goes ABOVE the copy and turns landscape — a 4:5
   portrait at full width would push the five ticks off a phone screen before
   anybody read one. */
@media (max-width: 860px){
  body.koda-page-home .svcsheet{ padding: 16px; align-items: start; }
  body.koda-page-home .svcsheet-card{
    grid-template-columns: minmax(0, 1fr); gap: 24px;
    padding: 28px 22px 26px; margin: 12px 0;
  }
  body.koda-page-home .svcsheet-shot{ order: -1; }
  body.koda-page-home .svcsheet-shot img{ aspect-ratio: 16 / 9; }
  body.koda-page-home .svcsheet-say h2{ font-size: 26px; }
}
@media (prefers-reduced-motion: reduce){
  body.koda-page-home .svcsheet-scrim,
  body.koda-page-home .svcsheet-card{ transition: none; }
  body.koda-page-home .svcsheet-card{ transform: none; }
}
/* ═══ THE FILM CARD IS A WIDER CARD (2026-09-16) ════════════════════════════
   Jeremy: *"Rug 'what you get' page — make the video roughly 2x larger (may
   need to increase that window size as needed)."*

   THE 370px COLUMN WAS SIZED FOR A PORTRAIT PLATE. A 4:5 photograph in it is
   463px tall and reads as the second half of the sheet; the same column given
   a 16:9 film is 208px tall, which is a thumbnail sitting in the corner of a
   980px card. The note above already records the trade ("the column is narrower
   than the photo would have been tall") — this is Jeremy taking the other side
   of it, and taking the window with him.

   SO THE ONE SERVICE THAT SHOWS A FILM GETS ITS OWN GEOMETRY. The card goes to
   1320px and the film column to 740px, which is 2.0x the width it had and 4.0x
   the area. `clamp()` rather than a flat 740 because the card is `min(1320px,
   100%)` and a fixed column on a card that has stopped being 1320 eats the copy
   instead: the 50vw middle term keeps the film at about half the
   screen all the way down, and the 370px floor means this can never come out
   SMALLER than the photograph column it replaced. At 1440 the film is 740 x 416;
   at 1280 it is 640; at 1000 it is the 500 it never was.

   THE TICKS GO TO ONE COLUMN. 460px of copy is not two columns of 16px list
   — §4.1 does not let the type shrink to make it fit, so the list changes shape
   instead.

   STACKED AT 1000, NOT 860. Below 1000 the copy column is the thing running out
   of room, and a film above the words at the full width of the card is bigger
   again than either arrangement of the two-column version. The generic stack
   rules further up stop at `.svcsheet-card` (0,2,1) and these carry `.has-film`
   as well (0,3,1), so they win wherever they overlap, whichever order they are
   read in. */
@media (min-width: 1001px){
  body.koda-page-home .svcsheet-card.has-film{
    width: min(1320px, 100%);
    grid-template-columns: minmax(0, 1fr) clamp(370px, 50vw, 740px);
  }
  body.koda-page-home .svcsheet-card.has-film .svcsheet-list{
    grid-template-columns: minmax(0, 1fr);
  }
}
@media (max-width: 1000px){
  /* `align-items: start` on the sheet, because a full-width film plus five
     ticks is taller than a laptop viewport and a centred card that overflows
     is a card with its own heading scrolled off the top. */
  body.koda-page-home .svcsheet:has(.svcsheet-card.has-film){ align-items: start; }
  body.koda-page-home .svcsheet-card.has-film{
    width: min(1320px, 100%);
    grid-template-columns: minmax(0, 1fr); gap: 24px;
  }
  body.koda-page-home .svcsheet-card.has-film .svcsheet-shot{ order: -1; }
}


/* ── Trust tiles ────────────────────────────────────────────────────────── */
/* FIVE TILES, AND FIVE IS AWKWARD — same problem the five problem cards have,
   same answer. `auto-fit` over a 1240px wrap gave four columns and stranded
   KODA4Kids alone on a second row, and at 238px "Truck-Mount" broke across two
   lines while every other value sat on one. A six-column grid with each tile
   spanning two gives three per row, ~390px each, and lets the fourth start at
   column 2 so the last pair centres under the three above it. */
body.koda-page-home .trust{
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 18px;
}
body.koda-page-home .tile{ grid-column: span 2; }
body.koda-page-home .tile:nth-child(4){ grid-column: 2 / span 2; }
@media (max-width: 1060px){
  body.koda-page-home .trust{ grid-template-columns: repeat(4, minmax(0, 1fr)); }
  body.koda-page-home .tile:nth-child(4){ grid-column: span 2; }
  body.koda-page-home .tile:nth-child(5){ grid-column: 2 / span 2; }
}
@media (max-width: 640px){
  body.koda-page-home .trust{ grid-template-columns: minmax(0, 1fr); }
  body.koda-page-home .tile,
  body.koda-page-home .tile:nth-child(4),
  body.koda-page-home .tile:nth-child(5){ grid-column: 1 / -1; }
}
/* THE TRUST TILES. Jeremy: "redesign the tile text/layout/fonts to be
   standardized (they have different sized fonts), to be visually appealing and
   engaging. Again, these are flat text on the tile. Very dull. No gold."

   THE TYPE IS NOW ONE SIZE. The old tiles auto-shrank their headline so that
   "1,100+", "5.0", "Family" and "KODA4Kids" could each fill the width — four
   different sizes across four cards sitting in a row, which is exactly the
   thing the eye reads as unfinished. Every value is now 34px, and the two long
   words are allowed to be long. Consistency across the row beats each card
   being individually optimal.

   THE STRUCTURE IS A LEDGER LINE, not a stack of centred text: the medallion
   and the value share the top row, the label sits under a rule, and the
   sentence closes it. Left-aligned, because centred text in four columns has
   no shared edge for the eye to run down.

   THE SURFACE IS LIT. A raised panel on the night band — a light top edge, a
   gradient that falls away downward, and a steel bar down the left that grows
   on hover. No gold anywhere: gold is the flag's, and spending it here would
   cost the flag its job. */
body.koda-page-home .tile{
  position: relative; overflow: hidden;
  padding: 22px 22px 20px 24px;
  background: linear-gradient(180deg, rgba(255,255,255,.115) 0%, rgba(255,255,255,.035) 100%);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: var(--radius);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.20), 0 18px 34px -20px rgba(0,0,0,.7);
  transition: transform .26s var(--ease), background .26s var(--ease);
}
body.koda-page-home .tile::before{
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--gold);
  transform: scaleY(.34); transform-origin: top;
  transition: transform .32s var(--ease);
}
body.koda-page-home .tile:hover{
  transform: translateY(-4px);
  background: linear-gradient(180deg, rgba(255,255,255,.17) 0%, rgba(255,255,255,.06) 100%);
}
body.koda-page-home .tile:hover::before{ transform: scaleY(1); }

/* The top row: medallion and value on one baseline. */
body.koda-page-home .tile-top{ display: flex; align-items: center; gap: 13px; margin-bottom: 14px; }
body.koda-page-home .tile-ico{
  width: 42px; height: 42px; border-radius: 50%; flex-shrink: 0;
  display: grid; place-items: center;
  background: rgba(201,164,76,.12);
  border: 1px solid rgba(201,164,76,.45);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.16);
  color: var(--gold-lit);
}
/* THE GOLD LINE IS THE HEADING (2026-09-11). It used to be a <p> with an
   uppercase 13px <h3> label under it, which meant the loudest thing in the tile
   was not the heading and the heading was not readable at arm's length. It is
   an <h3> now and it carries the claim — see the note in index.html — so the
   tile is two tiers instead of three: the claim, then the evidence.

   `.tile .tile-num`, not `.tile-num`: the rules below are element-scoped and
   would otherwise outrank a bare class, which is how the value silently shrank
   to 15.5px the last time. Specificity beat intent, which is the usual way a
   design note quietly stops being true. */
body.koda-page-home .tile .tile-num{
  font-family: var(--font-display); font-weight: 800; line-height: 1.08;
  /* A SIZE THAT HOLDS A PHRASE. 34px was set for "5.0" and "1,100+"; the line
     now says "1,200+ Reviews" and "Family Owned", which is three times the
     glyphs in a column 225px wide. The clamp keeps it as large as the widest
     of them can be without breaking mid-word. */
  font-size: clamp(24px, 2vw, 30px);
  /* GILT ON NIGHT. #c9a44c on #16202e measures 6.4:1, so the line that matters
     most in this section is also the warmest thing in it. Gilt is barred from
     small text on WHITE (2.37:1), never from display type on navy. */
  color: var(--gold-lit); letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  min-width: 0; overflow-wrap: anywhere;
}
/* The evidence, under the gilt hairline the label used to carry. */
body.koda-page-home .tile p{
  font-size: 16px; color: rgba(255,255,255,.8); line-height: 1.55;
  padding-top: 13px; border-top: 1px solid rgba(201,164,76,.28);
}

/* ── THE SHADE ─────────────────────────────────────────
   See the long note in index.html. The family tile used to FLIP; all five turn
   now, and the move is a shade drawn up from the foot of the tile.

   WHY THE FLIP HAD TO GO, BEYOND BEING ASKED. A flip is a 3D rotation, so it
   needs `perspective`, `preserve-3d` and `backface-visibility`, and all three
   are load-bearing — lose any one and the card squashes or shows both faces at
   ninety degrees. It also cannot hold an <iframe>: a YouTube embed on the back
   of a rotated card is rendered by a compositor that does not agree with the
   page about which way it is facing, and on the same card in Safari it simply
   is not there. Our Cause has to hold a film, so the move had to be flat.

   A TRANSLATION IS ALL IT IS NOW. The back sits one tile-height below its own
   box and slides to zero; the front lifts fourteen pixels and fades. `.tile`
   is already `overflow: hidden`, which is what turns the slide into a reveal.
   Two transforms and an opacity — every one of them a compositor property, so
   the whole thing is free, and an iframe inside it is just an iframe.

   THE BACK NEGATES THE PANEL'S PADDING (22/22/20/24, from .tile above) so a
   photograph, a video or a tinted panel runs to the tile's own rounded edge.

   AND IT CARRIES ITS OWN GROUND. The old flip let the band show through,
   because the back of a card that has rotated away IS the band. A shade is a
   surface sliding over another surface: it has to be opaque, or the front face
   reads through it for the whole half-second. So the back is painted with the
   tile's exact panel — the same white-on-navy gradient over --ink-mid — and
   the family photograph lands on precisely the tone it used to.

   THE GILT EDGE ALONG ITS TOP is the tell that something is arriving rather
   than something having been swapped, and `.tile::before` (the bar down the
   left) is lifted to z-index 3 so it stays lit through the whole move: the bar
   belongs to the TILE, and the tile has not gone anywhere. */
body.koda-page-home .tile-turn{
  display: block; text-align: left; color: inherit;
  font: inherit; width: 100%;
  -webkit-appearance: none; appearance: none;
}
body.koda-page-home a.tile-turn{ text-decoration: none; }
body.koda-page-home .tile-cause{ cursor: pointer; }
/* `height: 100%`, and it is load-bearing. The tile stretches to the tallest in
   its grid row but its own copy only fills about 95px of that, so without this
   the pair is 95px tall and the shade stops short of the foot. */
body.koda-page-home .tile-turn-in{ position: relative; height: 100%; }
body.koda-page-home .tile-face-front{
  transition: transform .46s var(--ease), opacity .3s var(--ease);
}
body.koda-page-home .tile-face-back{
  position: absolute; inset: -20px -22px -20px -24px;
  overflow: hidden;
  background:
    linear-gradient(180deg, rgba(255,255,255,.115) 0%, rgba(255,255,255,.035) 100%),
    var(--ink-mid);
  transform: translateY(100%);
  transition: transform .5s cubic-bezier(.2, .7, .25, 1);
  will-change: transform;
}
/* The gilt edge the shade arrives behind. */
body.koda-page-home .tile-face-back::after{
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: linear-gradient(90deg, var(--gold-lit) 0%, var(--gold) 46%, rgba(201,164,76,0) 100%);
  pointer-events: none;
}
body.koda-page-home .tile::before{ z-index: 3; }
body.koda-page-home .tile-turn:hover .tile-face-back,
body.koda-page-home .tile-turn:focus-visible .tile-face-back,
body.koda-page-home .tile-turn:focus-within .tile-face-back{ transform: translateY(0); }
body.koda-page-home .tile-turn:hover .tile-face-front,
body.koda-page-home .tile-turn:focus-visible .tile-face-front,
body.koda-page-home .tile-turn:focus-within .tile-face-front{
  transform: translateY(-14px); opacity: 0;
}
body.koda-page-home .tile-turn:focus-visible{ outline: 3px solid var(--gold-lit); outline-offset: 2px; }

/* `contain`, not `cover`. The tile is 401 × 163 — a 2.5:1 window — and the
   family is a 1.24:1 group: there is no crop of eight people that fills a shape
   that wide without throwing away half of them. So the photograph is fitted
   whole and anchored to the FOOT of the tile, which is where a group of people
   standing belongs; the navy either side of them is the panel, not a gap. */
body.koda-page-home .tile-family .tile-face-back img{
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; object-position: center bottom;
}

/* ── THE THREE BACKS THAT ARE COPY ─────────────────────────
   Set to the tile's own measure, centred vertically, and nothing in here is
   gold: the gilt line is the FRONT's job and spending it twice on one tile
   would tell the reader the two faces are two different objects. */
body.koda-page-home .tile-face-say{
  display: flex; flex-direction: column; justify-content: center;
  gap: 6px; padding: 13px 20px 13px 24px;
}
body.koda-page-home .tile-back-k{
  font-family: var(--font-body); font-size: 13px; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--gold-lit);
}
/* `.tile p.tile-back-p`, AND THE ELEMENT ON THE END IS THE WHOLE POINT.
   `body.koda-page-home .tile p` is (0,2,2) and a bare `.tile-back-p` is
   (0,2,1), so the front face's 16px body rule won and this panel silently
   rendered at 16 with a gilt rule above it that belongs to the OTHER face.
   That is the same failure the note by `.tile .tile-num` records, two hundred
   lines up, which is twice now: in this file, a class is not enough.

   15px, and it is a deliberate step down rather than a drift. CLAUDE.md 4.1
   floors web body at 16 and secondary at 14; this is the second tier of a
   panel whose first tier is the claim, read at hover distance on a tile the
   visitor chose to stop on. */
body.koda-page-home .tile p.tile-back-p{
  font-size: 15px; line-height: 1.45; color: rgba(255,255,255,.88);
  padding-top: 0; border-top: 0;      /* .tile p sets both; this is not that */
  margin: 0;
}
body.koda-page-home .tile-go{
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 15px; font-weight: 600; color: #fff;
  margin-top: 2px;
}
body.koda-page-home .tile-go-a{ transition: transform .26s var(--ease); }
body.koda-page-home .tile-turn:hover .tile-go-a{ transform: translateX(4px); }

/* ── THE FILM ON THE BACK OF OUR CAUSE ────────────────────
   The poster is a background on the face, so there is a picture on the tile
   from the first frame of the shade rather than a black rectangle. home.js
   sets --yt-poster from data-yt.

   THE IFRAME COVERS A SHAPE IT IS NOT. The tile is about 2.46:1 and the video
   is 16:9, so the player is laid out full width at its own aspect and centred
   on the vertical — which makes it taller than the tile at every tile height,
   with no magic number to keep in step. `.tile-face-back` is overflow: hidden,
   which does the cropping. */
body.koda-page-home .tile-face-film{
  background-image: var(--yt-poster, none);
  background-size: cover; background-position: center 42%;
}
body.koda-page-home .tile-film{ position: absolute; inset: 0; }
body.koda-page-home .tile-film iframe{
  position: absolute; top: 50%; left: 0;
  width: 100%; aspect-ratio: 16 / 9; height: auto;
  transform: translateY(-50%);
  border: 0; pointer-events: none;   /* the TILE takes the click, not the player */
}
/* A scrim under the two labels, or white type sits on whatever frame the video
   happens to be showing. */
body.koda-page-home .tile-face-film::before{
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(90deg, rgba(10,17,27,.72) 0%, rgba(10,17,27,.42) 46%, rgba(10,17,27,.12) 100%);
  pointer-events: none;
}
body.koda-page-home .tile-play{
  position: absolute; z-index: 2; left: 24px; top: 50%; transform: translateY(-50%);
  width: 52px; height: 52px; border-radius: 50%;
  display: grid; place-items: center;
  color: #fff;
  background: rgba(255,255,255,.14);
  border: 1px solid rgba(255,255,255,.5);
  box-shadow: 0 6px 18px -6px rgba(0,0,0,.7);
  transition: background .24s var(--ease), transform .24s var(--ease);
}
body.koda-page-home .tile-play svg{ margin-left: 3px; }  /* a triangle centres by its bulk, not its box */
body.koda-page-home .tile-cause:hover .tile-play{
  background: rgba(255,255,255,.26); transform: translateY(-50%) scale(1.06);
}
body.koda-page-home .tile-film-lbl{
  position: absolute; z-index: 2; left: 90px; top: 50%; transform: translateY(-50%);
  font-family: var(--font-display); font-weight: 800; font-size: 21px; color: #fff;
  text-shadow: 0 2px 10px rgba(0,0,0,.6);
}

/* ── THE SHADE UNDER prefers-reduced-motion ────────────────
   A cross-fade instead of a slide: the same information, no travel. The back
   still has to be opaque, so it is opacity that moves and not transform. */
@media (prefers-reduced-motion: reduce){
  body.koda-page-home .tile-face-front,
  body.koda-page-home .tile-face-back{ transition: opacity .28s var(--ease); }
  body.koda-page-home .tile-face-back{ transform: none; opacity: 0; }
  body.koda-page-home .tile-turn:hover .tile-face-front,
  body.koda-page-home .tile-turn:focus-within .tile-face-front{ transform: none; opacity: 0; }
  body.koda-page-home .tile-turn:hover .tile-face-back,
  body.koda-page-home .tile-turn:focus-within .tile-face-back{ transform: none; opacity: 1; }
}

/* ── ON A TOUCH SCREEN THERE IS NO HOVER ──────────────────
   `.is-open` is set by home.js from an IntersectionObserver on coarse
   pointers: the tile turns when it is the thing in the middle of the screen,
   which is the only "hover" a phone has. Without this the whole second face of
   five tiles — including the film — is unreachable on the device most of this
   traffic arrives on. */
body.koda-page-home .tile-turn.is-open .tile-face-back{ transform: translateY(0); opacity: 1; }
body.koda-page-home .tile-turn.is-open .tile-face-front{ transform: translateY(-14px); opacity: 0; }
@media (prefers-reduced-motion: reduce){
  body.koda-page-home .tile-turn.is-open .tile-face-back{ transform: none; }
  body.koda-page-home .tile-turn.is-open .tile-face-front{ transform: none; }
}

/* ═══ THE FILM LIGHTBOX ════════════════════════════════════
   Full bleed, black, one 16:9 frame inside it. It is its own scrim: there is
   nothing to see behind a video, and a second element for the ground would
   only be one more thing to keep in step with the dialog. */
body.koda-page-home .film{
  position: fixed; inset: 0; z-index: 1200;
  display: grid; place-items: center;
  padding: clamp(16px, 4vw, 48px);
  background: rgba(7,12,19,.92);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  opacity: 0; transition: opacity .26s var(--ease);
}
body.koda-page-home .film.on{ opacity: 1; }
body.koda-page-home .film-in{
  position: relative; width: min(1180px, 100%);
}
body.koda-page-home .film-frame{
  width: 100%; aspect-ratio: 16 / 9;
  background: #000; border-radius: var(--radius); overflow: hidden;
  box-shadow: 0 40px 90px -30px rgba(0,0,0,.9), 0 0 0 1px rgba(201,164,76,.38);
}
body.koda-page-home .film-frame iframe{ display: block; width: 100%; height: 100%; border: 0; }
/* ABOVE the frame on a wide screen, INSIDE its top-right corner once the frame
   has grown to the full width of the viewport and there is no "above" left. */
body.koda-page-home .film-x{
  position: absolute; right: 0; top: -52px;
  width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  color: #fff; background: rgba(255,255,255,.12);
  border: 1px solid rgba(255,255,255,.42);
  transition: background .2s var(--ease);
}
body.koda-page-home .film-x:hover{ background: rgba(255,255,255,.26); }
body.koda-page-home .film-x:focus-visible{ outline: 3px solid var(--gold-lit); outline-offset: 3px; }
@media (max-width: 700px){
  body.koda-page-home .film-x{ top: -50px; right: -2px; }
}
@media (prefers-reduced-motion: reduce){
  body.koda-page-home .film{ transition: none; }
}

/* ── Service area ───────────────────────────────────────────────────────── */
body.koda-page-home .area{ display: grid; grid-template-columns: 1fr 1.1fr; gap: 44px; align-items: center; }
@media (max-width: 900px) { body.koda-page-home .area{ grid-template-columns: 1fr; gap: 30px; } }
body.koda-page-home .cities{ display: flex; flex-wrap: wrap; gap: 9px; margin: 22px 0 24px; }

/* ── A HEAD CENTRES ONCE ITS COLUMN BECOMES THE PAGE (2026-09-17) ───────────
   Jeremy: *"When viewing the pre-login pages from a mobile device, some section
   headers are left justified while most are centered. Can you have everything
   consistent? Centered looks better on mobile."*

   HE IS DESCRIBING WHAT A COLLAPSED TWO-COLUMN BAND LEAVES BEHIND. Every band
   that is one column at any width — Why KODA, Services, Reviews, FAQ, the
   closing call — leads with `.sec-head.mid` and centres. Exactly two bands on
   this page are a claim set BESIDE something on a desktop, and those two are
   the ones Jeremy caught:

     · `.urine-say` — the pet claim, left of the picker and the dog.
     · the copy column of `.area` — left of the coverage map.

   Their heads are ranged left because they are aligned to the edge of a column
   that has a neighbour. Below 900px both grids collapse to `1fr` (see the two
   rules above this one and the `.urine` rule further up), the neighbour moves
   UNDER the claim rather than beside it, and the edge it was aligned to is now
   just the page margin — so the alignment is holding a relationship that no
   longer exists, and a reader thumbing down the page meets four centred heads
   and two ranged-left ones. Left was never the point; the neighbour was.

   THE BODY FOLLOWS THE HEAD. Centring a heading and leaving its paragraph
   ragged-left underneath reads as a mistake rather than as a choice, so the
   whole claim centres: eyebrow, heading, copy and button. The measure stays —
   `max-width` is what keeps a line readable and it is untouched; only the box
   it sits in moves to the middle. `.cities` is a row of pills rather than
   text, so it re-centres as a flex row.

   THE EYEBROW'S DASH GOES, which is not decoration-trimming: `.eyebrow::before`
   is a rule that runs off the LEFT of the words and reads as the start of a
   line. `.sec-head.mid` has hidden it on every centred head on this page since
   the day that class was written; these two now follow the same rule for the
   same reason. */
@media (max-width: 900px){
  body.koda-page-home .urine-say,
  body.koda-page-home .area > .rise:not(.map-frame){ text-align: center; }
  body.koda-page-home .urine-say .eyebrow::before,
  body.koda-page-home .area > .rise:not(.map-frame) .eyebrow::before{ display: none; }
  body.koda-page-home .urine-say p,
  body.koda-page-home .area > .rise:not(.map-frame) .lede{ margin-inline: auto; }
  body.koda-page-home .cities{ justify-content: center; }
}
body.koda-page-home .city{
  font-size: 16px; font-weight: 600; color: var(--ink); background: var(--paper);
  border: 1px solid var(--rule); border-radius: 999px; padding: 9px 17px;
  box-shadow: var(--lift-1), inset 0 1px 0 #fff;
}
/* ── THE MAP IS A PICTURE, NOT A LAYER OVER THE PAGE (2026-09-16) ──────────
   Jeremy: *"The plus/minus to zoom in/out of the map of the 'Serving West
   Michigan' section overlaps the top bar when users scroll."*

   LEAFLET'S CONTROLS ARE z-index 1000 AND THE BAR IS 900. `.leaflet-top` and
   `.leaflet-bottom` carry that 1000 in leaflet.css, and `.leaflet-container` is
   `position: relative` with NO z-index of its own — which does not open a
   stacking context — so those controls were not competing with the map, they
   were competing with the whole document. Scroll the coverage section under the
   fixed chrome and the zoom buttons painted straight through it, on top of the
   logo. `overflow: hidden` here never had anything to do with it: clipping is
   about geometry and this was about paint order.

   `position: relative` + `z-index: 0` makes this frame a stacking context, so
   every number inside it — the 1000 on the controls, the 400 on the tile panes,
   the labels — is resolved AGAINST THE FRAME and the frame itself sits at 0,
   below everything the page chrome owns. Nothing inside the map has to be
   renumbered, and nothing outside it can be reached from in there again. */
body.koda-page-home .map-frame{
  position: relative; z-index: 0;
  border-radius: var(--radius); overflow: hidden; border: 1px solid var(--rule);
  box-shadow: var(--lift-3); background: var(--page-deep);
}
body.koda-page-home .map-frame > div, body.koda-page-home .map-frame iframe{ display: block; width: 100%; height: 380px; border: 0; }
body.koda-page-home .map-fallback{ padding: 34px; font-size: 17px; color: var(--muted); display: grid; place-items: center; text-align: center; }

/* Leaflet's own tooltip chrome, restyled as a KODA label. Leaflet ships a white
   box with a grey border and a little arrow; left alone it looks like a browser
   tooltip sitting on the brand. !important because leaflet.css loads AFTER this
   file — it is injected at scroll time. */
.cov-label{
  background: var(--paper) !important; color: var(--ink) !important;
  border: 1px solid var(--rule-firm) !important; border-radius: 7px !important;
  box-shadow: var(--lift-2) !important;
  font-family: var(--font-body) !important; font-size: 14px !important; font-weight: 600 !important;
  padding: 5px 10px !important; white-space: nowrap;
}
.cov-label.is-hq{
  background: var(--ink) !important; color: #fff !important;
  border-color: var(--ink-deep) !important; font-weight: 700 !important;
}
.cov-label::before{ display: none !important; }   /* Leaflet's arrow */
.leaflet-container{ font-family: var(--font-body) !important; background: var(--page-deep) !important; }
.leaflet-control-attribution{ font-size: 12px !important; }

/* ── FAQ ────────────────────────────────────────────────────────────────── */
body.koda-page-home .faq{ display: grid; gap: 12px; max-width: 800px; margin-inline: auto; }
body.koda-page-home .faq details{
  background: var(--paper); border: 1px solid var(--rule); border-radius: var(--radius-sm);
  box-shadow: var(--lift-1); overflow: hidden; transition: box-shadow .22s;
}
body.koda-page-home .faq details[open]{ box-shadow: var(--lift-2); }
body.koda-page-home .faq summary{
  list-style: none; cursor: pointer; padding: 18px 54px 18px 20px; position: relative;
  font-family: var(--font-display); font-size: 19px; font-weight: 700; color: var(--ink);
}
body.koda-page-home .faq summary::-webkit-details-marker{ display: none; }
body.koda-page-home .faq summary::after{
  content: ''; position: absolute; right: 20px; top: 50%; width: 13px; height: 13px;
  margin-top: -7px;
  border-right: 2px solid var(--steel); border-bottom: 2px solid var(--steel);
  transform: rotate(45deg); transition: transform .26s var(--ease); transform-origin: 62% 62%;
}
body.koda-page-home .faq details[open] summary::after{ transform: rotate(-135deg); }
body.koda-page-home .faq p{ font-size: 17px; line-height: 1.65; color: var(--muted); padding: 0 20px 20px; }
body.koda-page-home .faq p a{ color: var(--steel-deep); font-weight: 600; }

/* ── Closing CTA, which is the top of the footer ────────────────────────────
   ONE FIELD, TWO HALVES. The band and the footer under it are the same navy
   and there is no edge between them: this one falls from the lift at its top to
   flat --ink at its foot, which is exactly what .foot is painted, so the
   boundary is invisible and the page ends in a single dark field rather than in
   two that nearly match. The radial is what stops that field reading as a flat
   wall — it puts the light where the headline is, which is the job the
   photograph was doing badly. See the note in index.html. */
/* ── A LIGHT GROUND, AND THE EDGE IS THE VALUE (2026-09-16) ─────────────────
   Jeremy: *"The Experience The KODA Difference section: give me a lighter
   coloured background/redesign. The feedback was that this blends too much into
   the bottom bar."*

   THE 09-11 PASS PUT THIS BAND AND THE FOOTER ON ONE NAVY FIELD ON PURPOSE, to
   cure a hard seam between two dark sections that almost matched. It worked, and
   it cost more than it bought: the page's last ask, its one button and its phone
   number ended up inside what a reader takes for the footer, and nobody reads a
   footer. The seam was the problem; the boundary was not.

   SO THE BAND IS LIGHT NOW AND THE FOOTER IS UNTOUCHED. White falling to the
   page's own mist, with `--weave` — the navy lattice every mist band on this
   page is printed with — pressed through it, so this is the page's texture
   rather than a new idea introduced in the last screen. Over that, the same
   pool of gilt light the dark version had, sitting where the headline is: it is
   what keeps a pale band from being a blank one, and it keeps the closing call
   related to every other gold object on the page.

   NO RULE ALONG THE FOOT, AND THAT IS THE POINT. Light-to-navy IS the edge —
   the same way every other section on this page ends — and it is the
   arrangement every OTHER pre-login page already had, where a light FAQ meets
   the navy footer and .foot-grid's gilt hairline is the first line inside it.

   WHAT `.band-night` WAS DOING FOR FREE, AND NOW IS NOT. It painted the
   heading, the eyebrow, the paragraph and the phone white, and pushed
   `.btn-ghost` onto an opaque white plate. Three of those five go back to the
   defaults they have everywhere else on this page — heading `--ink`, eyebrow
   `--steel-deep`, ghost button white paper on a firm rule — and only the
   paragraph and the phone are restated here, because only those two were
   written as literal whites. */
body.koda-page-home .close-band{
  padding: 76px 0 62px;
  color: var(--text);
  background:
    var(--weave),
    radial-gradient(86% 104% at 50% 20%,
      rgba(201,164,76,.26) 0%, rgba(201,164,76,.10) 42%, rgba(201,164,76,0) 74%),
    linear-gradient(178deg, #ffffff 0%, var(--page-deep) 62%, #e7ecf2 100%);
}
body.koda-page-home .close-cta{ text-align: center; }
body.koda-page-home .close-cta h2{ font-size: clamp(32px, 4vw, 48px); margin-bottom: 14px; color: var(--ink); }
/* --ink-soft, not --muted: this is the sentence the whole page closes on, and
   §4.1's reasoning about contrast for a 35+ audience applies to the last
   paragraph at least as much as to the first. */
body.koda-page-home .close-cta p{ font-size: 19px; color: var(--ink-soft); max-width: 54ch; margin: 0 auto 30px; }
/* THE NUMBER IS THE ONE PLACE GILT IS ALLOWED TO BE A LINE ON LIGHT GROUND.
   It is a 2px rule under 42px display type, not small text on white — the
   thing the token note warns about — and it is what carries the gold through
   from the pool above it to the footer's hairline below. */
body.koda-page-home .close-phone{
  display: inline-flex; align-items: center; gap: 13px;
  font-family: var(--font-display); font-size: clamp(30px, 4vw, 42px); font-weight: 800;
  color: var(--ink); margin-top: 26px; padding-bottom: 5px;
  border-bottom: 2px solid var(--gold);
  transition: border-color .2s, transform .2s, color .2s;
}
body.koda-page-home .close-phone:hover{ border-color: var(--gold-deep); color: var(--act); transform: translateY(-2px); }
body.koda-page-home .cta-row{ display: flex; gap: 13px; justify-content: center; flex-wrap: wrap; }
body.koda-page-home .band-night .btn-ghost{ background: rgba(255,255,255,.95); border-color: transparent; }

/* ── Footer ─────────────────────────────────────────────────────────────── */
/* THE FOOTER LIVES IN koda-chrome.css (2026-09-14). Every pre-login page
   wears it now, not just this one — see the header of that file. */

/* ── Reveal on scroll ───────────────────────────────────────────────────── */
/* REVEAL-ON-SCROLL IS ADDITIVE, NEVER SUBTRACTIVE.
   The hidden state is gated on `html.js`, which the inline script in <head>
   sets before first paint. Without JavaScript — or if home.js never lands, or
   if the IntersectionObserver is throttled because the tab is in the
   background — every section is simply visible. A page whose whole body stays
   invisible until an animation observer fires is one failed request away from
   a blank white screen, and a crawler sees that same blank screen. home.js
   carries a timeout that reveals anything still unseen, as a second net. */
body.koda-page-home .rise{ transition: opacity .7s var(--ease), transform .7s var(--ease); }
html.js body.koda-page-home .rise{ opacity: 0; transform: translateY(22px); }
html.js body.koda-page-home .rise.seen{ opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { html.js body.koda-page-home .rise{ opacity: 1; transform: none; } }
