/* ============================================================================
   Turtle Shell Home Protection — the redesigned home page
   ----------------------------------------------------------------------------
   Loaded on "/" only, through the layout's Styles section. Everything in here
   is namespaced .home-*: nothing on this page restyles a design.css class, so
   no other page can be reached from this file even by accident. The exceptions
   are stated where they happen, and every one of them is a design.css class
   reached THROUGH a .home-* ancestor:

     - two `.section.home-*` selectors, two classes deep, because design.css's
       `.section:nth-of-type(even)` is (0,2,0) and a single class would lose;
     - the shared form's parts inside `.home-estimate`, because the estimate
       card IS the lead form now and design.css dressed those parts for a white
       panel.

   Specificity note. This sheet is linked in the Styles section, which the head
   places BEFORE theme-dark.css. A tie therefore goes to theme-dark, so
   anything here that has to beat that file says so and carries the extra class
   to do it.

   The three colours are the client's, sampled from the mockups:
     #70953d  the hero's brand word, the estimate card's headings, the rule
     #21783c  the six values, the estimate card's border and its step shapes
     #000     the headline band, the values section, the photo grid

   Type. The mockups are set in a heavy display face. Until the site had
   webfonts this file reached for Georgia, on the grounds that adding one would
   mean a CDN or a new binary in the repo; both of those are settled now — the
   binaries are in /static/vendor/fonts and nothing is fetched from a CDN — so
   the display lines are set in Bebas Neue like the rest of the site's headings.
   See --home-display, and fonts.css for the family itself.

   EVERY MEASURED DIVISOR IN THIS FILE MOVED WITH IT. The hero headline and the
   six values are sized by dividing their column by the string's width in em,
   and Bebas Neue is roughly half Georgia Bold's width, so each of the seven
   numbers was re-measured against the shipped woff2 rather than scaled. The
   old figures are kept beside the new ones so the change is checkable.
   ============================================================================ */

:root {
  --home-lime: #70953d;
  --home-green: #21783c;
  --home-black: #000;

  /* The display face, which is now the site's heading face rather than a
     stack of its own: one family for "WE ARE TURTLESHELL", for FAITH and for
     every <h2> on the page. Written as var(--font-header) so it cannot drift
     from fonts.css — this is a name for the ROLE, not a second declaration of
     the family. */
  --home-display: var(--font-header);
}

/* The label on the hero's address field: present for a screen reader, absent
   for everyone else. design.css has no such class — .sr-only lives in
   admin.css, which this page does not load. */
.home-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---- C1. Hero ------------------------------------------------------------ */

.home-hero { background: var(--home-black); }

/* The headline band, and the container query container the heading is sized
   against. There is deliberately no .container between the two: 100cqw has to
   be the box the heading actually lays out in, or the heading is sized for one
   width and clipped at another — which is exactly what a .container in here did
   at 1440, where the band is 1392px wide and the column inside it 1192. */
.home-hero-banner {
  background: var(--home-black);
  padding: clamp(0.35rem, 1.4vw, 1rem) clamp(0.5rem, 1.6vw, 1.5rem) clamp(0.6rem, 1.8vw, 1.25rem);
  container-type: inline-size;
  /* A machine that never gets Bebas Neue falls through to Arial Narrow, which
     is wider, and a headline set to fill the band exactly would then spill the
     page. Clipped instead. */
  overflow: hidden;
}

/* "WE ARE TURTLESHELL" runs the full width of the band on one line, as the
   mockup has it. The divisor is measured rather than estimated: at font-size
   1em, in this case and letter-spacing, the string measures 6.85em wide in
   Bebas Neue, so 100cqw / 6.93 fills the band with about 1% to spare. (See the
   note over .home-value-word for why a per-character estimate is not good
   enough.) It was 12.60em / 12.75 in Georgia Bold.

   BOTH numbers moved, and the cap had to move with the divisor. Bebas Neue is
   condensed, so filling a 1392px band means a much larger font-size than
   Georgia needed — 200px at 1440 against 109px before — and the old 8.5rem cap
   would have bound at every desktop width and left the headline floating in
   the middle of the band, which is the one thing the mockup does not do. 12.5rem
   is that 200px: the headline still runs the full band up to about 1440 and
   caps beyond it, which is what the cap was for.

   font-weight is gone rather than set to 400. Bebas Neue has one weight, and
   fonts.css declares it across 100-900 precisely so that a weight here would be
   inert; saying nothing is the honest version of that. */
.home-hero-heading {
  font-family: var(--home-display);
  text-transform: uppercase;
  text-align: center;
  letter-spacing: 0.02em;
  line-height: 1.02;
  white-space: nowrap;
  margin: 0;
  color: #fff;
  font-size: min(calc(100cqw / 6.93), 12.5rem);
}

/* Outlined, against the solid green beside it. The plain colour is the
   fallback: paint-order and text-stroke are one @supports away from making the
   whole word invisible if an engine honours the transparent fill and not the
   stroke. */
.home-hero-we { color: #eef4ef; }

@supports (-webkit-text-stroke: 1px #fff) {
  .home-hero-we {
    color: transparent;
    -webkit-text-stroke: max(1.5px, 0.022em) #eef4ef;
    paint-order: stroke fill;
  }
}

.home-hero-brand { color: var(--home-lime); }

/* The tagline is INTER, and lower case, and it is the one place on this page
   where the mockup's treatment was not carried over.

   It is a hundred-character sentence — "We protect homes gutters in ways people
   love — through remarkable service and transparent pricing!" — and it was set
   uppercase because the serif it shared with the headline had nothing else to
   distinguish it. In a condensed display face at 12-18px that is a wall: no
   ascenders, no descenders, no word shapes, and the reader has to spell it.
   Bebas Neue is right one line above this and wrong here, so this line changes
   face and drops the caps with it.

   The tracking goes with the caps. 0.02em is the display face's; lower-case
   Inter wants nothing. */
.home-hero-tagline {
  font-family: var(--font-body);
  text-align: center;
  color: rgba(255, 255, 255, 0.94);
  line-height: 1.5;
  font-size: clamp(0.82rem, 1.5vw, 1.1rem);
  max-width: 60ch;
  margin: clamp(0.4rem, 1.2vw, 0.9rem) auto 0;
}

/* The photograph, and the estimate card centred on it. */
.home-hero-stage {
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: center;
  padding-block: clamp(1.5rem, 4vw, 3.5rem);
  background: var(--primary-dark);
}

.home-hero-bg { position: absolute; inset: 0; z-index: 0; }

.home-hero-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Dark enough for white text and a black card to sit on it. */
.home-hero-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(6, 12, 8, 0.42), rgba(6, 12, 8, 0.58));
}

/* min-width:0 is load-bearing, not tidiness. .home-hero-stage is a grid, and a
   grid item defaults to min-width:auto — so this container was sized by the
   estimate card's MIN-CONTENT width and held 390px inside a 375px viewport,
   spilling the page. The same trap design.css records against .lead-inner. */
.home-hero-inner { position: relative; z-index: 1; min-width: 0; }

/* ---- C1 + C2. The estimate card, which IS the lead form ------------------- */

/* One element does both jobs: <form id="lead-form"> wearing .home-estimate.
   app.js steps it, the mockup's step 1 is the card's opening state, and the
   card never moves — see _HomeLeadForm.

   The width is the mockup's: a focused panel, not a band. 36rem against the
   44rem this first shipped at, which read noticeably wider than the drawing at
   every width above 640px. */
.home-estimate {
  max-width: 36rem;
  min-width: 0;
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(0.55rem, 1.5vw, 0.9rem);
  background: var(--home-black);
  border: 3px solid var(--home-green);
  border-radius: 8px;
  padding: clamp(0.85rem, 2.2vw, 1.6rem);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
  color: var(--ink);
}

/* Both halves of the same trap. Grid items default to min-width:auto, and a
   <fieldset> additionally carries min-inline-size:min-content from the UA
   sheet — so without these the card is held open by the widest word in the
   longest checkbox label and spills a 375px viewport. */
.home-estimate > *,
.home-estimate .form-panel { min-width: 0; }

/* Steps 2-4 keep design.css's layout; only the colours it chose for a white
   panel need answering, and theme-dark.css has already answered most of them
   (legend, field backgrounds, the step numerals). What is left is the button
   row's own spacing, which assumed a gap-less parent. */
.home-estimate .form-nav { margin-top: 0; }

/* Step 1 is the mockup's card: title, sub-line, the photo panel, and nothing
   else. The shared Back/Continue/Submit row and the step dots belong to steps
   2-4 and stay out of the way until then — the visitor sees one button, Start,
   inside the address bar.

   BOTH rules key on .is-active, which app.js adds and _HomeLeadForm does NOT
   ship. That is the whole JavaScript-off contract: with no script the class
   never arrives, neither rule ever matches, every panel is visible and the
   submit button below is reachable. Do not add is-active to the markup.

   .form-nav uses a sibling combinator rather than :has() on purpose — it is
   the rule that can strand a visitor, so it must work in every engine. The
   dots use :has(), where an engine without it merely shows four dots one step
   early. */
.home-estimate .form-panel[data-step="0"].is-active ~ .form-nav { visibility: hidden; }
.home-estimate:has(.form-panel[data-step="0"].is-active) .form-steps { visibility: hidden; }

/* Both of those said `display: none` until the card was made a fixed box, and
   that is the one thing about them that changed: the dots and the button row
   are made INVISIBLE on step 1 now rather than removed, so the rows they sit in
   keep their height and the card does not shrink by ~110px on its first screen.
   What the visitor sees is the same, and the JavaScript-off contract above is
   untouched — neither rule matches without .is-active.

   Start still works through an invisible Continue: home.js calls next.click(),
   and .click() dispatches whatever the element's visibility. */

/* ---- The card is ONE BOX, whatever step is up ----------------------------- */

/* The width never moved — .home-estimate is a block-level box at max-width
   36rem, so it is 576px wherever the hero column is wider than that and the
   column's own width below. The HEIGHT moved by up to 185px between steps, and
   every section below the hero moved with it.

   The fix is to stop letting the current step decide how tall the card is. All
   four panels are placed in the SAME grid row, so the row is sized by the
   TALLEST of them and the three that are not current keep their box and lose
   their ink. Shorter steps therefore leave space under themselves rather than
   pulling the bottom of the card up. There is no measured height anywhere in
   here: add a field to step 3 and the row grows for all four steps at once.

   THE :has() GUARD IS THE JAVASCRIPT-OFF CONTRACT, the same one the two rules
   above keep. With no script no panel ever gets .is-active, nothing below
   matches, and the four panels stack down the page as they must — one grid row
   with nothing hiding three of them would draw all four on top of each other.
   An engine with no :has() support lands in the same place by the same route:
   the card resizes between steps as it did before, which is cosmetic, rather
   than the form becoming unreadable, which is not.

   THE COLUMN IS NOT OPTIONAL, and leaving it off is the trap here. `grid-row`
   alone gives an item a definite ROW and an auto COLUMN, and auto-placement
   then walks the four panels along that row into columns 1, 2, 3 and 4 — three
   of them implicit — instead of stacking them. The card came out four
   ~158px columns wide with step 1 wrapped to 1287px tall. `grid-area: 2 / 1`
   pins the column as well, which is what actually puts them in one cell. */
.home-estimate:has(.form-panel.is-active) .form-steps { grid-area: 1 / 1; }
.home-estimate:has(.form-panel.is-active) .form-panel { grid-area: 2 / 1; align-self: start; }
.home-estimate:has(.form-panel.is-active) .form-nav { grid-area: 3 / 1; }
.home-estimate:has(.form-panel.is-active) .form-status { grid-area: 4 / 1; }

/* Step 1 is the exception, and it is the reason the card is 70px shorter than
   the arrangement above alone would make it. Step 1 shows neither the dots nor
   the buttons, so if it sat in the panel row like the others it would push that
   row to ITS height and then leave the dots' row and the button row standing
   empty above and below it — 154px of black inside the mockup's opening card.

   So it spans all four rows instead, and it does so on EVERY step rather than
   only its own. That is what keeps the box a fixed size: the panel row is left
   to the three steps that do use the dots and the buttons, and step 1 is
   measured against the whole inner box rather than against one row of it. If it
   ever grows past that box it stretches the rows — equally on all four steps,
   because the span does not depend on which step is up.

   Centred rather than started, because what is left over is the room the dots
   and the buttons would have taken, and that room is above AND below it. */
.home-estimate:has(.form-panel.is-active) .form-panel[data-step="0"] {
  grid-area: 1 / 1 / 5 / 2;
  align-self: center;
}

/* A panel app.js has hidden keeps its cell. (0,3,1) against form-steps.css's
   `.form-panel[hidden]{display:none}` at (0,1,1), and design.css's own
   `display:grid` is what it goes back to.

   visibility:hidden is doing the accessibility work display:none used to: the
   panel is out of the tab order and off the accessibility tree, and the
   `hidden` attribute app.js sets is still on it saying the same thing. */
.home-estimate:has(.form-panel.is-active) .form-panel[hidden] {
  display: grid;
  visibility: hidden;
}

/* The button row is a grid rather than design.css's wrapping flex row, and the
   two buttons that are never up together share a column.

   That row was the card's last moving part. At 375 "Request My Free Inspection"
   does not fit beside Back, so on step 4 — and only step 4 — it wrapped onto a
   second flex line and the row went from 44px to 100px. Back takes column 1,
   Continue and Submit both take column 2, and the row is as tall as the taller
   of the two on every step.

   justify-self:end keeps Continue where design.css's `.form-next{margin-left:
   auto}` put it, and puts Submit in the same place. */
.home-estimate:has(.form-panel.is-active) .form-nav {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
}

.home-estimate:has(.form-panel.is-active) .form-back { grid-area: 1 / 1; }

.home-estimate:has(.form-panel.is-active) .form-next,
.home-estimate:has(.form-panel.is-active) .form-submit {
  grid-area: 1 / 2;
  justify-self: end;
}

/* Same trade as the panels, for the same reason: a button app.js has hidden
   keeps its cell and loses its ink. (0,3,1) against form-steps.css's
   `.form-nav [hidden]` at (0,1,1). */
.home-estimate:has(.form-panel.is-active) .form-nav [hidden] {
  display: flex;
  visibility: hidden;
}

.home-estimate-intro { gap: 0; }

/* THE SMALL CAPS ARE GONE, and so is the one font-family that covered all five
   of these. Both were a workaround for having a single face: the mockup's card
   is set in a caps-with-small-caps serif, and font-variant-caps was the only way
   to get two sizes of capital out of one stack. There are two faces now, so the
   card is split the way the mockup actually reads —

     the title    display type. It is the card's headline and the panel's
                  <legend>, four words, and it stays in the display face. (0,2,1)
                  below, because design.css's `.form-panel legend` is (0,1,1) and
                  fonts.css's is too.

     everything
     else        a sentence of instruction, three step captions, and an input
                  the visitor types their own street address into. Small caps in
                  a text field is not a style, it is a bug report waiting to
                  happen; all four are Inter, upright, mixed case. */
.home-estimate legend.home-estimate-title {
  font-family: var(--home-display);
}

.home-estimate-sub,
.home-estimate-t,
.home-estimate-field input {
  font-family: var(--font-body);
}

/* The card's title is the panel's <legend>: one title in the mockup, and the
   thing a screen reader should hear on entering the group. A legend is
   shrink-to-fit inside its fieldset, so centring it takes the explicit width.

   (0,2,1) to beat theme-dark.css's `…,.form-panel legend{color:#eaf5ec}` at
   (0,1,1) — that file is linked after this one. */
.home-estimate legend.home-estimate-title {
  display: block;
  width: 100%;
  color: var(--home-lime);
  text-align: center;
  font-weight: 400;
  letter-spacing: 0.01em;
  font-size: clamp(1.15rem, 3.1vw, 1.9rem);
  line-height: 1.2;
  padding: 0;
  margin: 0 0 0.3rem;
}

.home-estimate-sub {
  color: rgba(255, 255, 255, 0.92);
  text-align: center;
  font-size: clamp(0.85rem, 1.9vw, 1.12rem);
  letter-spacing: 0.01em;
  margin: 0 0 clamp(0.7rem, 1.8vw, 1.1rem);
}

.home-estimate-panel {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-radius: 4px;
}

.home-estimate-bg { position: absolute; inset: 0; z-index: 0; }

.home-estimate-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.home-estimate-scrim { position: absolute; inset: 0; background: rgba(8, 16, 10, 0.34); }

.home-estimate-body {
  position: relative;
  z-index: 1;
  padding: clamp(0.7rem, 2vw, 1.15rem);
}

/* The address field. A plain text input in a light bar — no dropdown, no
   suggestion list, nothing to attach a Places session token to.

   The `.home-estimate .field input` reach is because this input is now inside
   a .form-panel and design.css dresses those: width:100%, a border, a white
   background and .75rem of padding, all of which would draw a second box
   inside the bar. */
.home-estimate-field {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  background: rgba(255, 255, 255, 0.94);
  border-radius: 3px;
  padding: 0.25rem 0.35rem 0.25rem 0.7rem;
}

.home-estimate-pin { width: 20px; height: 20px; flex: none; color: #2a3b33; }

.home-estimate-field input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  min-height: 44px;
  padding: 0 0.25rem;
  color: #16211b;
  font-size: clamp(0.95rem, 2.2vw, 1.15rem);
  letter-spacing: 0.01em;
}

/* The focus indicator belongs to the BAR, not to the input inside it. An
   outline on the input is drawn INSIDE the white bar — that is where the heavy
   green ring through the middle of the field came from. A ring on the bar's own
   outside edge reads as the bar lighting up instead, which is what a white bar
   on a photograph can carry.

   TWO TONES, and both are load-bearing. The bar sits on an aerial photograph,
   so no single colour can promise SC 1.4.11's 3:1 against what is behind it —
   what is behind it changes from pixel to pixel. So the inner ring answers to
   the white bar (#21783c on #fff is 5.5:1) and the outer answers to the
   photograph (white on the scrimmed aerial), and the two answer to each other
   at that same 5.5:1. Whatever the photograph does, one of those boundaries is
   the indicator's edge, and every one of them passes.

   :focus-within rather than :focus-visible. design.css's site-wide
   `:focus-visible` rule is the keyboard contract and it still fires here — this
   is simply not a case where the mouse needs suppressing, because what the
   mouse now gets is a soft ring on the bar's edge rather than a hard outline
   through the middle of the field. Both routes light the same thing.

   The transparent outline on the input is for forced-colors mode, where
   box-shadow is dropped entirely and a transparent outline is the one thing the
   system repaints in the highlight colour. Without it Windows High Contrast
   would have no focus indicator on this field at all. It is deliberately NOT
   gated on .is-autofocus below: in forced-colors the ring is gone and the
   system's own highlight is all a visitor has, so it may as well arrive early.

   .is-autofocus is what keeps the ring off on arrival. app.js focuses this
   field the moment the page loads, which drew the ring before the visitor had
   touched anything; home.js marks the card while that focus is still the
   script's and unmarks it on the first real pointerdown or keydown, so a click
   or a Tab lights the bar and a page load does not. See home.js. */
.home-estimate-field { transition: box-shadow var(--dur) var(--ease), background-color var(--dur) var(--ease); }

.home-estimate:not(.is-autofocus) .home-estimate-field:focus-within {
  background: #fff;
  box-shadow:
    0 0 0 2px var(--home-green),
    0 0 0 5px rgba(255, 255, 255, 0.9);
}

/* The ring above replaces this outline; it is kept, transparent, for the
   forced-colors case. (0,2,1) with :focus-visible, so it also out-ranks
   design.css's `:focus-visible{outline:3px solid var(--accent)}`. */
.home-estimate-field input:focus,
.home-estimate-field input:focus-visible {
  outline: 2px solid transparent;
  outline-offset: 0;
}

.home-estimate-field input::placeholder { color: #4a5a52; opacity: 1; }

.home-estimate-go { flex: none; }

/* The three steps, drawn as the mockup's little houses. clip-path only — the
   graphic is three pentagons and a numeral, and an image of that would be one
   more asset to keep in step with the copy inside it. */
.home-estimate-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.3rem, 1vw, 0.75rem);
  margin: clamp(0.7rem, 1.8vw, 1.1rem) 0 0;
  padding: 0;
}

.home-estimate-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  min-width: 0;
  background: var(--home-green);
  color: #fff;
  text-align: center;
  padding: clamp(0.9rem, 3vw, 1.4rem) clamp(0.25rem, 1vw, 0.6rem) clamp(0.6rem, 1.6vw, 0.9rem);
  clip-path: polygon(50% 0, 100% 24%, 100% 100%, 0 100%, 0 24%);
}

.home-estimate-n {
  display: grid;
  place-items: center;
  width: clamp(1.4rem, 3.4vw, 1.9rem);
  height: clamp(1.4rem, 3.4vw, 1.9rem);
  border-radius: 50%;
  background: #fff;
  color: var(--home-green);
  font-family: var(--font-header);
  font-weight: 800;
  font-size: clamp(0.72rem, 1.9vw, 1rem);
  line-height: 1;
  flex: none;
}

.home-estimate-t {
  font-size: clamp(0.62rem, 1.55vw, 0.92rem);
  line-height: 1.25;
  letter-spacing: 0.02em;
  overflow-wrap: anywhere;
}

/* The footer bar: badge, promise, and the small print under it. */
.home-estimate-foot {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: clamp(0.4rem, 1.4vw, 0.8rem);
  margin: clamp(0.7rem, 1.8vw, 1.1rem) 0 0;
  padding: clamp(0.4rem, 1.4vw, 0.7rem) clamp(0.7rem, 2vw, 1.2rem);
  background: rgba(0, 0, 0, 0.9);
  border-radius: var(--r-pill);
  text-align: center;
}

.home-estimate-badge {
  grid-row: 1 / 3;
  width: clamp(28px, 6vw, 40px);
  height: auto;
  flex: none;
}

.home-estimate-foot-lead {
  font-family: var(--font-header);
  font-weight: 800;
  text-transform: uppercase;
  color: #fff;
  line-height: 1.2;
  font-size: clamp(0.74rem, 2vw, 1.05rem);
}

/* "Quick. Easy. No appointment necessary." — three sentences at nine to twelve
   pixels. The lead above it stays in the display face because it is four words
   and twice the size; this one is below the floor where a condensed uppercase
   face can still be read at a glance, so it is Inter and mixed case. */
.home-estimate-foot-sub {
  font-family: var(--font-body);
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.82);
  line-height: 1.35;
  font-size: clamp(0.62rem, 1.4vw, 0.78rem);
}

/* The rule that makes the form step at all — `.form-panel[hidden]` — used to
   live here, for the home page only. It is site-wide now: see form-steps.css,
   linked on every page from _Layout, and ORIGINAL-SITE-DEFECTS.md §11. */

/* ---- C3. The promo video -------------------------------------------------- */

/* IT IS NO LONGER EDGE TO EDGE, and that is the substantive change here.

   Full-bleed cost the top and bottom of the picture and bought nothing. The
   file is 1280x720, so 16:9 exactly; the old rule gave it the full viewport
   width and then capped its height at min(80vh, 42rem), which at 1440x900 is a
   1440x672 box — 2.14:1 — and object-fit:cover cropped a tenth of the frame off
   the top and bottom of a video whose subject is a roofline. A contained 16:9
   panel shows the whole frame and crops nothing.

   What replaces the bleed is a frame: the column's width, a real aspect box, a
   hairline in the brand green, a corner radius and a shadow, so the poster
   reads as a still that was placed there rather than as a video element that
   happens to be paused. The height cap survives as a max-width on the figure —
   80vh of height, expressed as the width that produces it at 16:9 — which does
   the same job without fighting the aspect-ratio. */

.home-video-kicker {
  font-family: var(--home-display);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  text-align: center;
  color: var(--home-lime);
  font-size: clamp(0.72rem, 1.5vw, 0.9rem);
  margin: 0 0 0.35rem;
}

.home-video-figure {
  max-width: min(100%, calc(80vh * 16 / 9));
  margin: clamp(1.1rem, 3vw, 2rem) auto 0;
}

.home-video-frame {
  position: relative;
  isolation: isolate;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 10px;
  border: 1px solid rgba(33, 120, 60, 0.55);
  background: var(--home-black);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.42);
}

/* object-fit:cover with a 16:9 source in a 16:9 box crops nothing — it is here
   only for the pixel the aspect box and the intrinsic size can disagree by. */
.home-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  margin: 0;
  border: 0;
  border-radius: inherit;
  background: var(--home-black);
}

/* ---- The controls --------------------------------------------------------- */

/* This overlay ships `hidden` and home.js unhides it, which is the same trade
   _HomeLeadForm makes with .form-submit: the <video> ships carrying the
   `controls` attribute so a visitor with no JavaScript gets the browser's own
   player, and the script removes that attribute at the moment it reveals this.
   Neither state has no controls, and neither state has two sets of them. */
.home-video-ui {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  /* A click-through sheet: only the controls inside it take events, so the
     browser's own gestures on the video still reach the video. */
  pointer-events: none;
}

/* display:grid above would beat the UA's `[hidden]{display:none}`, so the
   attribute has to be honoured here or the overlay ships visible. */
.home-video-ui[hidden] { display: none; }

.home-video-ui > * { pointer-events: auto; }

/* The scrim under the bottom bar, and — until the video starts — a wash across
   the whole poster for the play button to sit on. Both are on the overlay
   rather than on the video, so neither can tint a picture that is playing. */
.home-video-ui::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(60% 60% at 50% 42%, rgba(0, 0, 0, 0.22), transparent 70%),
    linear-gradient(to top, rgba(0, 0, 0, 0.72) 0, rgba(0, 0, 0, 0.28) 22%, transparent 48%);
  transition: opacity var(--dur) var(--ease);
}

.is-playing .home-video-ui::before {
  background: linear-gradient(to top, rgba(0, 0, 0, 0.66) 0, transparent 34%);
}

/* The centred play affordance and its caption, hidden once the video is
   running — the bar along the bottom is enough then, and a shape in the middle
   of the picture is not. visibility as well as opacity, so it leaves the tab
   order along with the view. */
.home-video-cue {
  display: grid;
  justify-items: center;
  gap: 0.55rem;
  transition: opacity var(--dur) var(--ease), visibility var(--dur);
}

.is-playing .home-video-cue {
  opacity: 0;
  visibility: hidden;
}

.home-video-big {
  display: grid;
  place-items: center;
  width: clamp(3.4rem, 9vw, 5rem);
  height: clamp(3.4rem, 9vw, 5rem);
  padding: 0;
  border: 2px solid rgba(255, 255, 255, 0.92);
  border-radius: 50%;
  background: rgba(33, 120, 60, 0.86);
  color: #fff;
  cursor: pointer;
  transition: transform var(--dur) var(--ease), background var(--dur) var(--ease);
}

.home-video-big:hover {
  background: var(--home-green);
  transform: scale(1.06);
}

.home-video-big svg {
  width: 42%;
  height: 42%;
  fill: currentColor;
  /* Optically centred: a triangle's bounding box is not its centre of mass. */
  margin-left: 8%;
}

/* Hidden on a phone and back at md. The frame is 16:9 of a 343px column there
   — 193px tall — and once the 44px control bar has taken its share there is not
   the height left for a caption under the play button without the two reading
   as one crowded stack. The lead paragraph above the frame says "Thirty-five
   seconds" in words either way, so nothing is lost. See the md block. */
.home-video-meta {
  display: none;
  margin: 0;
  font-family: var(--home-display);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: clamp(0.66rem, 1.4vw, 0.82rem);
  color: rgba(255, 255, 255, 0.9);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
}

.home-video-bar {
  position: absolute;
  left: clamp(0.5rem, 1.6vw, 1rem);
  right: clamp(0.5rem, 1.6vw, 1rem);
  bottom: clamp(0.5rem, 1.6vw, 1rem);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.home-video-toggle,
.home-video-sound {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: 44px;
  padding: 0 0.85rem;
  border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.8rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.home-video-toggle { min-width: 44px; padding-inline: 0.6rem; }

.home-video-toggle:hover,
.home-video-sound:hover { background: rgba(0, 0, 0, 0.78); }

/* The unmute control is the one the brief asks to be unmissable, and while the
   sound is off it is the only filled thing on the picture: a word rather than a
   glyph, and at the opposite end of the bar from play/pause so it cannot be
   read as part of it. It goes quiet once the sound is on, because at that point
   it is only a mute button.

   IT IS WHITE RATHER THAN BRAND GREEN, which is not the first thing to try and
   is what the poster settles. The last second of this film — and its poster
   frame — is a full-width green banner reading TURTLESHELLHOME.COM, in very
   nearly this green, and a green pill sitting on it disappeared into it at
   exactly the moment the control matters most. White on dark green is the
   highest contrast available here and it cannot collide with a frame. */
.home-video-sound {
  margin-left: auto;
  background: #fff;
  border-color: #fff;
  color: var(--primary-dark);
  font-weight: 700;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
}

.home-video-sound:hover { background: #eaf5ec; }

.is-sound .home-video-sound {
  background: rgba(0, 0, 0, 0.55);
  border-color: rgba(255, 255, 255, 0.5);
  color: #fff;
  font-weight: 600;
  box-shadow: none;
}

.is-sound .home-video-sound:hover { background: rgba(0, 0, 0, 0.78); }

.home-video-toggle svg,
.home-video-sound svg {
  width: 1.1rem;
  height: 1.1rem;
  fill: currentColor;
  flex: none;
}

/* One icon per state, both in the markup, the wrong one display:none. Swapping
   an SVG path from script would put the icon in two places. */
.home-video-i-pause,
.is-playing .home-video-i-play,
.home-video-i-on,
.is-sound .home-video-i-off { display: none; }

.is-playing .home-video-i-pause,
.is-sound .home-video-i-on { display: block; }

/* ---- C5. Why Choose Us ---------------------------------------------------- */

/* Two classes deep on purpose: design.css paints every even .section with
   --bg-soft at (0,2,0), and a bare .home-why would lose to it wherever this
   section lands in the order.

   The padding is this section's own, and shorter than --section-y
   (clamp(2.5rem,6vw,5rem)). The block is meant to read as one dense object,
   and half the density is how little room there is around it. */
.section.home-why {
  background: var(--home-black);
  overflow: hidden;
  padding-block: clamp(1.6rem, 3.6vw, 2.75rem);
}

.home-why-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(0.7rem, 2vw, 1.25rem);
  align-items: center;
}

/* The rotated "WHY CHOOSE US?" and the rule beside it. Below 900px it is an
   ordinary heading above the values — a 90-degree label needs a column to
   stand in and a phone has none to spare. */
.home-why-title {
  font-family: var(--home-display);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #fff;
  font-size: clamp(0.8rem, 2.2vw, 1rem);
  margin: 0;
  text-align: center;
}

/* The six values as one block. gap:0 and a line-height well under 1 are the
   design, not an accident: in the mockup the words very nearly touch, and any
   air between them reads as a list instead.

   0.78 survived the change of face by luck rather than judgement, and it is
   worth writing down why it did not have to move: it was set against Georgia
   Bold's 0.692em cap height and Bebas Neue's is 0.700em, so the gap between the
   bottom of one word's capitals and the top of the next one's goes from about
   0.088em to about 0.080em. Uppercase throughout, so no descender ever reaches
   into the line below.

   max-width is what the brief called "a narrower column than you used". It
   binds at 768 (where the container is 728px wide and the words would
   otherwise be set at 185px) and does not bind at 375 or inside the three-
   column desktop layout, so one declaration covers all three widths. */
.home-values {
  container-type: inline-size;
  display: grid;
  gap: 0;
  padding: 0;

  /* width:100% is not redundant beside max-width, and leaving it out cost an
     hour. `container-type:inline-size` brings inline-size CONTAINMENT: the
     box's own width may not be computed from its contents. A grid item with
     auto margins is sized fit-content rather than stretched — and fit-content
     of a contained box is ZERO, which set every word to font-size 0 and
     collapsed the block to nothing. A definite width restores stretch and lets
     the auto margins do the centring instead. */
  width: 100%;
  max-width: 30rem;
  margin: 0 auto;

  /* The prefix column is fixed so the words' own column is a known width, and
     the type below can be sized as an exact fraction of it. */
  --lead-col: 3.4rem;
  --lead-gap: 0.5rem;
}

.home-value {
  display: grid;
  grid-template-columns: var(--lead-col) minmax(0, 1fr);
  column-gap: var(--lead-gap);
  align-items: center;
  min-width: 0;
}

/* The six are three groups — we HAVE faith, transparency and integrity; we TAKE
   accountability; we ARE excellent and results-oriented — and the mockup reads
   them that way. This is the air above the second and third groups, and it is
   the only thing that separates them: no rule, no break, and inside a group the
   lines stay exactly as tight as they were.

   About a third of a line, measured. The six line boxes are 0.78 of their own
   font sizes and those are fractions of the words' column, which works out at
   an average line of 0.116 of that column — so a third of one is 0.039 of it,
   and once the prefix column and its gutter are taken off the container that is
   3.2cqw. Written in cqw, like every other size in this block, so it keeps that
   proportion at 375, 768 and 1440 rather than being right at one of them: it
   lands on 11.0px, 15.4px and 15.4px against average lines of 32.5px, 47.1px
   and 47.1px.

   It costs the block about 11% of its height, which is the price of the
   grouping and is as small as the grouping can be drawn.

   The prefix rides its own group's first line — it is in the same <li> as that
   line's word — so the margin moves the two together and "we take" and "we are"
   stay aligned to the words they introduce. */
.home-value.is-group-start { margin-top: 3.2cqw; }

/* "we have", "we take", "we are" — 10 to 12px, and the only lower-case-shaped
   text in the block. Inter: at that size the display face's capitals are a row
   of strokes. Uppercase and tracked, which is what an eyebrow label wants and
   what the mockup draws. */
.home-value-lead {
  grid-column: 1;
  justify-self: end;
  font-family: var(--font-body);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #fff;
  font-size: clamp(0.6rem, 1.3vw, 0.72rem);
  line-height: 1.1;
  white-space: nowrap;
}

/* Every value fills the same column, so how long the word is decides how big
   it is — which is the whole visual idea of the mockup: FAITH enormous,
   RESULTS-ORIENTED small, and the six lines flush at both edges.

   --w is the words' column, and each divisor below is that word's own measured
   width in em — rendered in this face, case and letter-spacing — so --w over it
   is the size that fills the column exactly.

   Measured per word, not counted per character: INTEGRITY and EXCELLENT are
   both nine letters and differ by 8% (3.27em against 3.52em), so a per-character
   rule sets one of them visibly wrong. Each divisor carries about 2% of slack,
   and .section.home-why clips rather than spills on a machine that never gets
   Bebas Neue and falls through to Arial Narrow, which is wider.

   ALL SIX WERE RE-MEASURED FOR BEBAS NEUE, and the Georgia figures are kept in
   the right-hand column so the move is checkable. Bebas Neue is a little over
   half Georgia Bold's width at the same size, so every word is set roughly
   1.9 times larger to fill the same column — which is the whole of why this
   block is now about twice as tall as it was. Nothing else about it changed:
   the words are still flush at both edges, still nearly touching, still sized
   only by how long they are. That is the design; the face just made it bigger.

   Measured with HarfBuzz against the shipped woff2, at letter-spacing 0.02em,
   the same way the Georgia numbers were — reproducing those to within 0.07% is
   what says the method is the same one. */
.home-value-word {
  grid-column: 2;
  display: block;
  min-width: 0;
  text-align: center;
  font-family: var(--home-display);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 0.78;
  white-space: nowrap;
  color: var(--home-green);
  --w: calc(100cqw - var(--lead-col) - var(--lead-gap));
}

/*                              measured  with slack        was (Georgia) */
.home-value-faith { font-size: calc(var(--w) / 1.83); }           /*  1.79   3.52 */
.home-value-transparency { font-size: calc(var(--w) / 5.02); }    /*  4.92   9.11 */
.home-value-integrity { font-size: calc(var(--w) / 3.34); }       /*  3.27   6.23 */
.home-value-accountability { font-size: calc(var(--w) / 5.40); }  /*  5.29  10.00 */
.home-value-excellent { font-size: calc(var(--w) / 3.59); }       /*  3.52   6.67 */
.home-value-results { font-size: calc(var(--w) / 6.17); }         /*  6.05  11.40 */

.home-why-mascot {
  width: 100%;
  max-width: clamp(9rem, 40vw, 14rem);
  height: auto;
  margin-inline: auto;
}

/* ---- C6. Project Photos --------------------------------------------------- */

.section.home-photos { background: var(--home-black); }

.home-photos-title {
  font-family: var(--home-display);
  text-transform: uppercase;
  text-align: center;
  color: #fff;
  letter-spacing: 0.02em;
  font-size: clamp(1.5rem, 4.2vw, 3rem);
  margin: 0 0 clamp(1rem, 3vw, 2.25rem);
}

/* auto-fit against a 22rem floor: one column on a phone, two at 768, three on
   a 1200px container. Five photographs land as three and two, six as three and
   three, and a seventh needs no change here. */
.home-photos-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: clamp(0.6rem, 1.6vw, 1.25rem);
  margin: 0;
  padding: 0;
}

.home-photo { min-width: 0; }

/* No link and no lightbox — these are photographs and nothing else. */
.home-photo-img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 4px;
  background: #12241a;
}

/* ============================================================================
   Breakpoints
   ============================================================================ */

/* md ≥768 — the estimate card gets its own margin off the photo's edges, and
   the values pick up the wider prefix column the mockup has. */
@media (min-width: 768px) {
  .home-values { --lead-col: 4rem; --lead-gap: 0.6rem; }
  .home-hero-stage { min-height: clamp(24rem, 52vh, 36rem); }

  /* 414px of frame at this width, against 193 on a phone. Room for it. */
  .home-video-meta { display: block; }
}

/* lg ≥900 — the values block takes the mockup's three-column shape: the
   rotated label, the words, the mascot.

   max-width on the inner grid is the other half of "narrower than you used".
   The container is 1200-1320px wide here; the block is 54rem, centred inside
   it, which is what leaves the words at about 440px rather than 720. */
@media (min-width: 900px) {
  .home-why-inner {
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 14rem);
    gap: clamp(0.9rem, 2.2vw, 1.75rem);
    max-width: 54rem;
  }

  .home-why-title {
    position: relative;
    align-self: stretch;
    display: grid;
    align-items: center;
    padding-right: clamp(0.6rem, 1.3vw, 1.1rem);
    font-size: clamp(0.78rem, 1.1vw, 1rem);
  }

  /* Reading bottom-to-top, as the mockup sets it. */
  .home-why-title-text {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
  }

  /* The thin rule down the left of the block. Drawn on the heading rather than
     the list so it spans the whole section height whichever of the three
     columns is taller. */
  .home-why-title::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: 2px;
    background: var(--home-lime);
  }

  /* Full height of the block, as the mockup has it: stretched to the row the
     values set, and contained inside it so the turtle is never cropped. */
  .home-why-mascot {
    align-self: stretch;
    max-width: 100%;
    height: 100%;
    margin-inline: 0;
    object-fit: contain;
    object-position: center bottom;
  }
}

/* xl ≥1280 — the hero photograph gets the height the mockup gives it. */
@media (min-width: 1280px) {
  .home-hero-stage { min-height: clamp(28rem, 56vh, 42rem); }
}
