/*
 * Homepage stylesheet — the viewport-height hero system and the CHOOSE A
 * CATEGORY band.
 *
 * Served as a static asset and pulled in by `<HomeStyles>` rather than
 * `import`ed, for exactly the reasons given at the top of chrome.css: a bundled
 * import attaches the chunk to the shared `[brand]` layout segment and Next then
 * emits a <link> to it on every brand's every page. A hoisted <link> to a public
 * asset is rendered only by the page that needs it, which is what keeps a brand
 * that wears the default homepage byte-identical.
 *
 * Transplanted from design/produnk/HomeB.dc.html — "HOMEPAGE — WORKING (synced
 * from the live demo)", which canvas.json names as the homepage board — with its
 * ultrawide companion HomeBWide.dc.html, and from design/produnk/
 * _live_hero_demo.html, which is the FUNCTIONAL artifact: the boards freeze the
 * hero at one photo height apiece (470px at 1440, 830px at 2560) and only the
 * demo carries the flex system, the anchor regimes and the resize behaviour.
 * Where the two disagree on a value that is drawn, THE BOARD WINS (playbook §3);
 * where the board cannot express a behaviour, the demo is the source.
 *
 * Class prefix `sf-home-`.
 *
 * THE SECTION HEADER AND THE REVIEW BAR LEFT THIS SHEET on 2026-08-26. Both are
 * PLATFORM patterns (tokens §4) and this sheet had carried THREE copies of the
 * header (the band's, the review bar's, Live Orders') beside category.css's and
 * photos.css's. All of it is now `public/storefront/sections.css` under the
 * `sf-sec-` prefix, rendered by `<SectionStyles>` from the components that draw
 * it; the review bar's placement here rides the `--band` modifier.
 *
 * THE ANATOMY (tokens §4 "Homepage hero system"): one window's height is the
 * header, then the photo, then the band. THE BAND IS NEVER SACRIFICED — the
 * board's own PRIORITY RULE, in Ryan's words: "the photo takes only the
 * remainder (photo min-height gives way before the band ever slips below the
 * fold) — the band is the bottom edge of the first screenful, the photo takes
 * the rest." That is why the hero has a DEFINITE height and the photo shrinks
 * into it, rather than a `min-height` the photo could push past.
 */

/* ------------------------------------------------------- the focus ring -- */

/*
 * EVERY INTERACTIVE CONTROL IN THIS SHEET (Ryan's ruling, 2026-08-26 — the
 * platform pattern, its three grounds and the contrast arithmetic are
 * documented once, at the top of chrome.css).
 *
 * TWO CONTROLS, and both are on light ground, so both take the default accent
 * ring — accent #C4530A is 4.42:1 on paper #FBFBFA, 4.58:1 on surface #FFFFFF,
 * both well clear of the 3:1 bar. (The review bar's link and its mobile quote
 * strip were the other two until 2026-08-26; they left with the pattern, and
 * their rings are declared in sections.css beside it.):
 *
 *   · the category band's tile links, on the page's paper;
 *   · the Live Orders pins, which sit on the map band's white ground and on the
 *     drawn land #EDECE7 — 4.44:1 against that land, measured, so the default
 *     ring holds there too and the on-media PAIR is not needed. THE GOOGLE MAPS
 *     GROUND DOES NOT CHANGE THAT, and it was checked rather than assumed: the
 *     muted style paints land #EDECE7 and water #FFFFFF — the same two values
 *     the board draws (`home-map-style.ts`, held to this sheet by
 *     `tests/home-map-google.test.ts`) — so both measurements above still
 *     describe the ground the pins sit on. The on-media pair becomes the
 *     answer only if a later pass turns on satellite or terrain imagery, which
 *     would be a line in THIS block and nowhere else.
 *
 * The hero holds no controls of its own; its headline and sub are text and its
 * photo is a photo. New control in this sheet: add it HERE rather than beside
 * its component, so there is one place to read the rule off.
 *
 * The `var()` fallbacks repeat the platform values exactly once, so this sheet
 * still draws a compliant ring if it is ever loaded without chrome.css.
 */
.sf-home-band__card:focus-visible,
.sf-home-live__pin:focus-visible {
  outline: var(--sf-focus-ring, 2px solid var(--brand-color-accent, #c4530a));
  outline-offset: var(--sf-focus-ring-offset, 2px);
}

/* ------------------------------------------------------- the hero system -- */

/*
 * HOW MUCH OF THE WINDOW THE CHROME ALREADY OWNS.
 *
 * The header is a sibling of `<main>`, not of this section, so the hero cannot
 * measure it — it can only be told. The bar's height is already published on
 * `:root` by chrome.css (`--sf-pinned-offset`, 54px / 50px at the 920 cut / 54px
 * again at the mobile cut, tracked there); the MASTHEAD's is not, because its
 * readers were all inside the header's own subtree until this page existed.
 *
 * So the masthead is MIRRORED here, at exactly the two values chrome.css
 * declares (82px, 74px at the 1024 cut) and zero below the mobile cut where
 * `.sf-header__full` is `display: none` and the bar IS the header. A mirror is a
 * thing that can drift, so it is ASSERTED: `tools/hgate.mjs --chrome` reads the
 * rendered header's height at every rung of the ladder and fails if it is not
 * exactly what this computes. The alternative — publishing the total from
 * chrome.css — is a chrome.css edit, and this pass is gated on that file being
 * untouched.
 *
 * `--sf-pinned-offset` is read with a `0px` fallback for the same reason
 * chrome.css reads it that way: a brand that does not wear this chrome never
 * loads that file.
 */
.sf-home-hero {
  --sf-home-masthead: 82px;
  --sf-home-chrome: calc(var(--sf-home-masthead) + var(--sf-pinned-offset, 0px));

  /*
   * The photo's PREFERRED share of the remainder, and its hard floor.
   *
   * Neither is a minimum on the first screenful: the flex line below gives the
   * band its whole natural height first and hands the photo what is left, so on
   * a short window the photo is smaller than both of these and the band still
   * ends exactly at the fold. The board's own crop-math table says the same
   * thing in numbers — "1366 × 768 laptop … photo flexes to ~410px".
   *
   * The floor is the demo's 300px (280px at the mobile regime): the point below
   * which the photograph stops being a photograph, at which the hero gives up
   * and grows past the fold because there is no arrangement that fits.
   */
  --sf-home-photo-floor: 300px;

  /*
   * THE DEFINITE HEIGHT, and it is deliberate.
   *
   * A `min-height` here would be the §5 gotcha's advice and the wrong shape for
   * this box: with an indefinite height a flex line never SHRINKS, so the photo
   * would hold its floor and push the band below the fold on every laptop. The
   * height is definite so the photo can give way, and `min-height: max-content`
   * puts the gotcha's guarantee back — the box still grows when its CONTENT
   * needs more than the window has (a wrapped band, a two-line label), which is
   * the case the gotcha was written for.
   *
   * `100dvh`, not `100vh`: on a phone the two differ by the browser's own
   * collapsing chrome, and the band is measured to the fold.
   */
  background: var(--brand-color-paper, #fbfbfa);
  display: flex;
  flex-direction: column;
  height: calc(100dvh - var(--sf-home-chrome));
  /* The boards declare no leading, so every line box that does not name one is
   * `normal`; globals.css's 1.65 body leading would otherwise deepen the
   * headline and every card label. Same reason as chrome.css and category.css. */
  line-height: normal;
  /* The prefixed value first, for the Safari versions that took `max-content`
   * on a height only under `-webkit-`. A browser that understands neither
   * ignores both and the hero clips on a short window instead of growing — the
   * same failure the §5 gotcha names, so it is worth the one extra line. */
  min-height: -webkit-max-content;
  min-height: max-content;
}

@media (max-width: 1023.98px) {
  .sf-home-hero {
    --sf-home-masthead: 74px;
  }
}

@media (max-width: 767.98px) {
  .sf-home-hero {
    /* `.sf-header__full` is `display: none` at this cut — the bar is the whole
     * header, and `--sf-pinned-offset` alone is the chrome's share. */
    --sf-home-masthead: 0px;
  }
}

/*
 * THE MEDIA BOX — the photograph and the headline that sits over it.
 *
 * It is a column of its own rather than one box because of the mobile regime:
 * below 640 the headline DROPS BELOW THE PHOTO, and a headline that has to be
 * both inside the photo's clip and after it in flow would be two copies of the
 * same <h1>. One copy, two positions: the text is `position: absolute` inside
 * this box on the desktop regime and a static flex item under the frame on the
 * mobile one.
 *
 * `flex: 1 1 0` — basis ZERO, so the box's SIZE is the remainder and not its
 * content. Its automatic minimum is left alone (no `min-height: 0`), which is
 * what protects the mobile regime: below 640 the headline is a real flex item in
 * here, and a media box allowed to shrink past its content would squeeze the
 * copy rather than the photograph.
 */
.sf-home-hero__media {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  position: relative;
}

/* The photograph's clip. Full-bleed by construction — the media box is a child
 * of the hero, which is a child of `.main`, and nothing in that chain has a
 * measure. */
.sf-home-hero__frame {
  background: var(--brand-color-ink, #17181a);
  flex: 1 1 0;
  /*
   * THE LAST THING STANDING. Everything above this line hands the band its full
   * height first, so the photograph is the remainder — but the remainder cannot
   * go to zero: on the desktop regime the headline is v-centred INSIDE this box
   * and a frame shorter than the headline block would spill white type onto the
   * paper band below it. Measured, the block is 292px at 1440.
   *
   * So this is where the priority rule stops. Above `chrome + floor + band` the
   * band ends exactly at the fold at every height; below it the floor holds and
   * the band is the thing that slips. At 1440 that threshold is 778px of window,
   * and the numbers at every rung are in the pass report.
   */
  min-height: var(--sf-home-photo-floor);
  overflow: hidden;
  position: relative;
}

/*
 * The hero master, covering the frame.
 *
 * `--sf-home-anchor-x` is written by script, twice: a blocking inline `<script>`
 * at the end of the hero computes it BEFORE FIRST PAINT, and `<HomeHeroAnchor>`'s
 * effect recomputes it on every resize. The right-crop guard that keeps source
 * x 5346 visible needs a length divided by a length, which CSS cannot do, which
 * is why any of that exists.
 *
 * THE FALLBACK IS THE NO-SCRIPT ANCHOR, and it is PER REGIME. 35% left-bias /
 * 44% down is the board's own static value and it is right where the board drew
 * it — but it is a DESKTOP value, and the mobile regime does not crop anywhere
 * near there: at 390 the mobile rule resolves to 96.654%, so a no-script phone
 * used to render a crop 289 CSS px away from the one Ryan approved, showing the
 * blurred left of the master instead of the goal/kids cluster. The 640 block at
 * the bottom of this sheet restates this declaration with that regime's own
 * fallback; the derivation is there.
 *
 * A no-script DESKTOP page still renders the board's own anchor at every width,
 * which is the plainest correct hero rather than a broken one — the guard only
 * moves the crop on windows tall enough to crop the master horizontally, and
 * on those the 35% is a composition the board itself drew, not a defect.
 */
.sf-home-hero__photo {
  display: block;
  height: 100%;
  inset: 0;
  object-fit: cover;
  object-position: var(--sf-home-anchor-x, 35%) 44%;
  position: absolute;
  width: 100%;
}

/*
 * THE HEADLINE BLOCK, v-centred on the left of the photo.
 *
 * V-CENTRED, not at the board's drawn top. HomeB freezes it at `top: 118px` in a
 * 470px photo and HomeBWide at `top: 310px` in an 830px one, which are two
 * instances of one rule and neither is a constant this build could hold. The
 * rule is written on HomeBWide itself: "Headline v-centered left with a
 * left-edge scrim — the safe zone moves WITH the vertical center, which is
 * exactly where the photo keeps its subjects." The demo implements exactly that
 * (`top: 50%; translateY(-50%)`), and the photo's height here is never either
 * board's number anyway.
 */
.sf-home-hero__text {
  /*
   * CENTRED BY LAYOUT, NOT BY `translateY(-50%)`, and that is a measured
   * decision rather than a stylistic one.
   *
   * A half-percentage translate lands the block on a fractional offset and
   * Chrome composites the transformed subtree, which rasterises its text against
   * the LAYER's rounded origin instead of the document's. The block's own
   * `getBoundingClientRect` still reads whatever you would expect — so the
   * damage is invisible to measurement and shows up only in pixels: cropped
   * against the board, the subline's FIRST line came out exactly 1px low while
   * its second and third lines were pixel-identical (2.077% on the headline
   * region). Stretching the box over the frame and centring the child inside it
   * is pure layout, no layer, and the same crop reads 0.000%.
   */
  align-items: center;
  display: flex;
  inset: 0;
  position: absolute;
}

/*
 * The measure. `.sf-container` rather than the demo's flat `left: 24px`, so the
 * headline stands on the same content edge as the header above it and the band
 * below it at every rung of the site's gutter ladder (64 / 40 / 32 / 15).
 */
.sf-home-hero__text-inner {
  align-items: flex-start;
  display: flex;
  flex-direction: column;
}

.sf-home-hero__headline {
  color: var(--brand-color-surface, #ffffff);
  /* globals.css styles every `h1`/`h2`/`h3` — its own typeface, weight 500,
   * letter-spacing, `line-height: 1.12` and `text-wrap: balance`. The board
   * draws a plain div, so every one of those is overridden here rather than
   * inherited — `text-wrap` included, below. Same reset, same reason, as
   * `.sf-sec-head__title` (sections.css). */
  font-family: var(--brand-font-body, system-ui, sans-serif);
  /*
   * 64px is the board's, at 1440 AND at 2560 — it is the cap, not a step on a
   * ramp. 4.6vw is the demo's ramp verbatim; it crosses 64 at 1391, so above
   * that width the cap is what shows and below it the demo's scaling is. (The
   * demo's own 66px ceiling is the one number here that no board draws.)
   */
  font-size: clamp(40px, 4.6vw, var(--text-display));
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 0.98;
  margin: 0;
  /* globals.css balances every h1/h2/h3; no board asks for it, and here it
   * MATTERS: below 640 this line is "HEAVY-DUTY HOOPS." in one box and the
   * board draws its break after HEAVY-DUTY. Balancing moves that break.
   *
   * `wrap` — the initial value, and the one that turns balancing off. This read
   * `normal` until 2026-08-26, which is not a `text-wrap` keyword (it belongs to
   * `white-space`), so the declaration was dropped at parse time and globals.css's
   * `balance` was in force the whole time the comment claimed it had been reset.
   * It cost nothing at today's copy — measured identical at ten widths — because
   * `balance` and `wrap` can only differ on a line that wraps, and this one does
   * not until the copy grows. The comment is what was load-bearing; now the
   * declaration is too. */
  text-wrap: wrap;
}

/*
 * "HOOPS." — its own line, and on the mobile regime the accent.
 *
 * `display: block` is what breaks the line, so the two regimes differ by a
 * `display` and a `color` rather than by two spellings of the headline. The
 * board's desktop line is `HEAVY-DUTY<br>HOOPS.` in one white; the demo's mobile
 * line is `HEAVY-DUTY HOOPS.` with the second word in accent — tokens §4:
 * "HOOPS. carries the accent only there".
 */
.sf-home-hero__headline-accent {
  color: inherit;
  display: block;
}

.sf-home-hero__sub {
  color: rgba(255, 255, 255, 0.88);
  font-size: clamp(15.5px, 1.35vw, 18px);
  line-height: 1.65;
  margin: 18px 0 0;
  max-width: 540px;
}

/* ------------------------------------------------- CHOOSE A CATEGORY band -- */

/*
 * `flex: none` is the whole priority rule in one declaration: the band takes its
 * natural height off the top of the line and the photo divides what is left.
 */
.sf-home-band {
  background: var(--brand-color-paper, #fbfbfa);
  flex: none;
  padding: 48px 0 26px;
}

/*
 * THE SECTION HEADER LEFT THIS SHEET on 2026-08-26. It was the platform pattern
 * (tokens §4) duplicated from category.css declaration for declaration, held to
 * that copy by a parity test; three MORE copies of it lived in this sheet and in
 * photos.css. All of them are now one block in `public/storefront/sections.css`
 * under the `sf-sec-` prefix, and the markup renders `.sf-sec-head*`.
 *
 * The band's own head is the pattern with nothing added, so nothing about it
 * remains here.
 */

.sf-home-band__cards {
  display: flex;
  gap: 16px;
  list-style: none;
  margin: 28px 0 0;
  padding: 0;
}

/*
 * The list item is the FLEX ITEM; the link inside it is the card.
 *
 * The board draws five bare divs. Five links to five categories are a list, so
 * the markup is one — and that puts an `<li>` between the row and the card the
 * row is sizing. The item carries the track and the card fills it, so the
 * rendered boxes are the board's and the semantics are the page's.
 */
.sf-home-band__item {
  display: flex;
  flex: 1 1 0;
  min-width: 0;
}

.sf-home-band__card {
  background: var(--brand-color-surface, #ffffff);
  border: var(--border-hairline);
  border-radius: var(--radius-floaty);
  box-shadow: 0 4px 14px rgba(23, 24, 26, 0.06);
  /* The whole tile is the link — the board draws a card, and a card whose photo
   * and label are not both part of the target is a smaller hit area than it
   * looks. */
  color: inherit;
  display: block;
  flex: 1;
  min-width: 0;
  padding: 10px 10px 12px;
  text-decoration: none;
}

.sf-home-band__card:hover .sf-home-band__label,
.sf-home-band__card:focus-visible .sf-home-band__label {
  color: var(--brand-color-accent, #c4530a);
}


.sf-home-band__photo {
  border-radius: var(--radius-control);
  display: block;
  /* The board's 148px at 1440; the demo's ramp below it (10.5vw crosses 148 at
   * 1410). */
  height: clamp(88px, 10.5vw, 148px);
  object-fit: cover;
  width: 100%;
}

.sf-home-band__label {
  color: var(--brand-color-ink, #17181a);
  display: block;
  font-size: var(--text-support);
  font-weight: 600;
  margin-top: 10px;
  text-align: center;
}

/* The chevron the mobile list rows wear. It has no job on the card composition,
 * and an empty box in the flow would still take the gap. */
.sf-home-band__chevron {
  display: none;
}

/* --------------------------------------------- the demo's two-up, DROPPED -- */

/*
 * THE DEMO STACKS THE CARDS TWO-UP BELOW 900, AND THIS BUILD DOES NOT. That is
 * the one behaviour of the approved demo this pass deliberately does not carry,
 * and it was decided by measurement rather than taste — playbook §5.
 *
 * Two-up puts five cards on three rows. Measured at 900 x 900 with that rule in
 * place: the band stands 589.5px tall, which leaves the photograph 186px of a
 * 776px hero — and the desktop headline block, which is 199px at that width and
 * v-centred INSIDE the photograph, does not fit in it. The composition breaks
 * before the reader ever scrolls. Five-up at the same width keeps the band near
 * 300px and the photo near 470.
 *
 * WHY THE DEMO DOES NOT SHOW THIS: its header is 56px and the real chrome's is
 * 136px, so every photo on this site is 80px shorter than the demo's math. The
 * two-up cut was drawn against a hero that had the room.
 *
 * Five-up therefore runs all the way down to the hero's own 640 cut, where the
 * cards become list rows and the question disappears. IT IS A FLAGGED RULING,
 * not a settled one: the numbers for both arrangements are in the pass report.
 */

/* ----------------------------------------------- the platform mobile head -- */

/*
 * IT LEFT WITH THE PATTERN (2026-08-26). The mobile section header — smaller
 * circle, smaller mark, the title one step down the scale — breaks at the SITE's
 * mobile cut (767.98) and not at the hero's own 640, because it belongs to the
 * section-header pattern that every mobile board draws and not to this hero.
 * That is exactly why it is in sections.css now.
 */

/* ------------------------------------------------------ the 640 regime -- */

/*
 * THE HERO'S OWN CUT, and it is NOT the site's mobile cut.
 *
 * 640 is what tokens §4 ratifies and what the demo implements: below it the
 * photograph is too busy to carry type, so the headline drops out of the photo
 * and lands under it in ink, "HOOPS." takes the accent, the crop re-centres on
 * the goal/kids cluster (source x 4434 — `<HomeHeroAnchor>`), and the five cards
 * become list rows.
 *
 * The site's mobile cut is 767.98 and has been since the chrome pass moved it
 * there. So 641–767 renders THE DESKTOP HERO UNDER THE MOBILE CHROME, and that
 * band is a FLAGGED OPEN RULING, not a decision taken here: the numbers are in
 * the pass report. It is built at 640 because that is what is ratified.
 *
 * `max-width: 640px` and not `639.98px` is also deliberate and also flagged: the
 * site's convention gives a named cut to the WIDER side, but the demo's regime
 * test is `window.innerWidth <= 640` and `<HomeHeroAnchor>` has to agree with
 * this stylesheet exactly or a viewport of 640 gets a centred crop under an
 * overlaid headline. The two are written to the same number; which number it
 * should be is part of the same ruling.
 */
@media (max-width: 640px) {
  .sf-home-hero {
    --sf-home-photo-floor: 280px;
  }

  /*
   * THE MOBILE REGIME'S NO-SCRIPT ANCHOR.
   *
   * Same declaration as above with a different FALLBACK — the variable still
   * wins whenever either computer has run, so this is only ever what a phone
   * with no JavaScript renders. It exists because the base rule's 35% is a
   * DESKTOP value: the mobile rule centres the crop on source x 4434 and at 390
   * that resolves to 96.654%, so no-script used to land 289 CSS px away from the
   * approved cluster, on the sacrificial blur at the left of the master.
   *
   * 96.654% is the value the regime resolves to at 390 x 844 — the modal phone,
   * and the width this pass verified first paint at. The whole regime is a
   * function of WIDTH alone here: the photo is at its 280px floor on every phone
   * (measured, at 844 and at every taller viewport), so the frame's box does not
   * move with the window's height. Across the phone range the exact value runs
   * 90.595% at 320 to 100% at 430 and above, and this one fallback is within
   * 33 CSS px of the exact crop at the far end of that — against the 289–300px
   * the 35% was wrong by. The three decimals are the driver's own
   * `toFixed(3)`, so a script that lands writes the same shape of value.
   *
   * `tools/hgate.mjs anchor` proves it with script execution DISABLED rather
   * than trusting this comment.
   */
  .sf-home-hero__photo {
    object-position: var(--sf-home-anchor-x, 96.654%) 44%;
  }

  .sf-home-hero__text {
    /* Out of the photo and into the flow, under the frame. It stays a flex row
     * — with the box no longer stretched over the frame, centring its one
     * full-width child is a no-op. */
    padding: 18px 0 4px;
    position: static;
  }

  .sf-home-hero__headline {
    color: var(--brand-color-ink, #17181a);
    font-size: 34px;
    line-height: 1.02;
  }

  .sf-home-hero__headline-accent {
    color: var(--brand-color-accent, #c4530a);
    /* One line, two words: the break is a desktop-only device. */
    display: inline;
  }

  .sf-home-hero__sub {
    color: var(--brand-color-ink-soft, #44464a);
    font-size: var(--text-emphasis);
    margin-top: 10px;
    max-width: none;
  }

  .sf-home-band {
    padding: 14px 0 18px;
  }

  .sf-home-band__cards {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 14px;
  }

  .sf-home-band__card {
    align-items: center;
    display: flex;
    gap: 12px;
    padding: 8px 12px 8px 8px;
  }

  .sf-home-band__photo {
    flex: none;
    height: 60px;
    width: 92px;
  }

  .sf-home-band__label {
    flex: 1;
    margin-top: 0;
    text-align: left;
  }

  .sf-home-band__chevron {
    color: var(--brand-color-faint, #9a988f);
    display: block;
    flex: none;
    font-size: var(--text-product);
    line-height: 1;
  }
}

/* ================================================================ reviews == */

/*
 * THE REVIEW BAR LEFT THIS SHEET on 2026-08-26, with the section header, to
 * `public/storefront/sections.css` under the `sf-sec-` prefix. It was a PLATFORM
 * pattern (tokens §4) drawn on this page and on the category page from two
 * boards, shipped as two copies held together by a pairwise parity test; there
 * is one copy now, and the test is retired with the duplication it guarded.
 *
 * WHERE IT SITS is the only thing this board ever said about it that the
 * category board did not, and that survives as a MODIFIER the markup renders:
 * `.sf-sec-reviews--band` — its own section on the page's paper, 84px below the
 * CHOOSE A CATEGORY band, and the platform's mobile section rhythm below the
 * cut.
 */

/* ============================================================ live orders == */

/*
 * THE LIVE ORDERS SECTION (tokens §4 "Live Orders map"). Transplanted from
 * HomeB.dc.html for everything that is DRAWN, and from
 * design/produnk/_live_hero_demo.html — which the board's own caption names as
 * the functional artifact — for everything that MOVES.
 *
 * Header and sub line on the page measure; then a FULL-BLEED band, hairline top
 * and bottom, 480px of white, holding the LIVE chip, the drawn ground, and the
 * pins.
 *
 * THE GROUND IS A PHASE. The ratified end state is Google Maps with a muted
 * custom style and a camera that pans toward each fresh drop (design notes
 * 2026-08-24). Nothing in this block draws a map: the outline is one <svg> the
 * markup owns, and every rule here is about the BAND, the CHIP, the PINS and
 * the CARDS — the things that stay when the ground changes.
 */

.sf-home-live {
  /* The board declares no leading on this section either — the boards declare
   * none on any section, so every line box that does not name one is `normal`
   * and globals.css's body 1.65 would otherwise deepen the whole band. */
  line-height: normal;
  margin-top: 84px;
}

/* The section header is the platform's, and it is `.sf-sec-head` in
 * sections.css — this section was the fifth copy of it until 2026-08-26. */

/*
 * The sub line, INDENTED PAST THE ICON CIRCLE. 54px is the platform's own
 * number for this (tokens §3, `--space-icon-indent`) and it is also what the
 * board draws — 40px of circle plus the header's 14px gap, which is why the
 * platform number is 54 in the first place. Written through the token so the
 * two cannot diverge.
 */
.sf-home-live__sub {
  color: var(--brand-color-muted, #6c6a62);
  font-size: var(--text-body);
  margin: 10px 0 0 var(--space-icon-indent);
}

/*
 * THE BAND. Full-bleed by construction: it is a child of the section rather
 * than of `.sf-container`, so it spans the viewport while the header above it
 * keeps the page measure.
 *
 * `position: relative` is LOAD-BEARING twice over — the pins are absolutely
 * positioned against it, and it is a clipping container, so the rule that every
 * `overflow != visible` box hosting absolute descendants must be positioned
 * (playbook §5, the sr-span overflow bug) applies to it by name.
 */
.sf-home-live__bleed {
  /*
   * THE DRAWN GROUND'S LAND COLOUR, and it is NOT a palette token. #EDECE7 sits
   * between `well` #F1F0EC and `line` #E8E7E3 and appears nowhere else on the
   * site; it is the board's map ART, drawn once, for a surface scheduled to
   * become a Google Maps custom style. Naming it locally is the honest
   * treatment — it is not promoted to brand.json, and it retires with the
   * outline. FLAGGED as a board colour outside the vocabulary.
   */
  --sf-home-map-land: #edece7;

  background: var(--brand-color-surface, #ffffff);
  border-bottom: var(--border-hairline);
  border-top: var(--border-hairline);
  /* 480px of white PLUS the two hairlines. The boards set no `box-sizing`
   * reset, so their `height: 480px` is the CONTENT box and the 1px borders are
   * added outside it — 482 as rendered. This site is border-box, so the
   * rendered number is the one to write; the same conversion the mobile quote
   * strip's 82% took, and the same reason. */
  height: 482px;
  margin-top: 26px;
  overflow: hidden;
  position: relative;
}

.sf-home-live__ground {
  display: block;
  height: 100%;
  width: 100%;
}

/*
 * THE LIVE CHIP. Board-verbatim, and it is a status rather than a control, so
 * it takes no focus treatment.
 */
.sf-home-live__chip {
  align-items: center;
  background: var(--brand-color-surface, #ffffff);
  border: var(--border-hairline);
  border-radius: var(--radius-control);
  color: var(--brand-color-ink, #17181a);
  display: inline-flex;
  font-family: var(--brand-font-mono, ui-monospace, monospace);
  font-size: var(--text-micro);
  gap: 8px;
  left: 20px;
  letter-spacing: 0.12em;
  margin: 0;
  padding: 6px 12px;
  position: absolute;
  top: 16px;
  z-index: 3;
}

.sf-home-live__dot {
  background: var(--brand-color-live, #e5484d);
  border-radius: var(--radius-pill);
  display: inline-block;
  height: 10px;
  position: relative;
  width: 10px;
}

/*
 * THE PULSE. The DEMO draws it and the board does not — a board cannot draw a
 * pulse — and tokens §4 asks for "a LIVE chip w/ pulsing `live` dot", so it
 * ships. It is why the chip's pixel gate hides this pseudo-element and says so.
 */
.sf-home-live__dot::after {
  animation: sf-home-live-pulse 1.6s ease-out infinite;
  border: 2px solid color-mix(in srgb, var(--brand-color-live, #e5484d) 55%, transparent);
  border-radius: var(--radius-pill);
  content: "";
  inset: -5px;
  position: absolute;
}

@keyframes sf-home-live-pulse {
  0% {
    opacity: 1;
    transform: scale(0.55);
  }

  100% {
    opacity: 0;
    transform: scale(1.7);
  }
}

/*
 * THE PIN LAYER. It fills the band and it is what the provider measures: the
 * projection is expressed against THIS box, so the ground and the pins can
 * never be measuring two different rectangles.
 *
 * `pointer-events: none` on the layer with the pins taking them back, so the
 * layer does not sit over the band swallowing everything.
 */
.sf-home-live__layer {
  inset: 0;
  pointer-events: none;
  position: absolute;
}

/*
 * THE GOOGLE MAPS CANVAS — the ratified ground (design notes 2026-08-24), and
 * the ONE rule this whole phase added to the drawing.
 *
 * It is always in the markup and EMPTY unless a map was resolved: no key, or a
 * script that does not arrive, and this div paints nothing at all while the
 * drawn outline below it carries the section exactly as it does today. That is
 * the degrade, and it is the absence of a swap rather than a fallback mode.
 *
 * `z-index: 0` is the whole layering argument in one declaration. It puts the
 * canvas above the drawn `<svg>` ground, which is static and therefore paints
 * below every positioned box, and below the pins (2), the chip (3) and the
 * cards (4) — the numbers this sheet already used. It also opens a stacking
 * context, so the API's own absolutely positioned furniture cannot climb over a
 * pin however it numbers itself.
 *
 * POINTER EVENTS COME BACK ONLY WHEN IT IS LIVE, and that is Google's terms of
 * service: the attribution mark and the Terms link the API injects must remain
 * clickable. The layer above turns them off for everything else; the pins take
 * their own back, and being painted above they win the hit test anyway.
 */
.sf-home-live__canvas {
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: 0;
}

.sf-home-live__canvas[data-active="true"] {
  pointer-events: auto;
}

/* Text a reader gets from the drawing and a listener cannot: the pin buttons'
 * names, and the polite announcement of each new order. Same treatment as
 * `.sf-sec-reviews__spoken`, which shared this rule until the review bar left
 * for sections.css on 2026-08-26. */
.sf-home-live__spoken {
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

/* Pinned to the layer's own origin so the clipped box cannot land somewhere
 * that would extend the band's scrollable area. */
.sf-home-live__announce {
  left: 0;
  margin: 0;
  top: 0;
}

/*
 * THE PIN. Board geometry exactly — 26 x 34, and the negative margins are what
 * make `left`/`top` address the pin's POINT rather than its box, which is the
 * coordinate the projection produces and the one the cast shadow flares from.
 *
 * It is a BUTTON and the board draws a div: the pins carry the only information
 * in this section, so they are operable and named (each holds a clipped
 * sentence). The UA button styling is reset to nothing so the drawing is the
 * board's; the focus ring is in this sheet's focus block.
 */
.sf-home-live__pin {
  background: none;
  border: 0;
  cursor: pointer;
  height: 34px;
  margin: -34px 0 0 -13px;
  padding: 0;
  pointer-events: auto;
  position: absolute;
  transition: opacity 0.9s;
  width: 26px;
  z-index: 2;
}

.sf-home-live__pin svg {
  display: block;
  filter: drop-shadow(0 2px 2px rgba(23, 24, 26, 0.35));
  height: 100%;
  width: 100%;
}

/*
 * THE TAPERED, BLURRED CAST SHADOW — the pushpin treatment, and it is DRAWN ON
 * THE BOARD, so it is lifted rather than invented: a 38 x 15 box rotated 32°
 * about its right edge, blurred 2.5px, filled with a radial gradient that is
 * densest at the pin's point, and clipped to a ten-vertex taper. It reads as a
 * shadow cast by a pin stuck INTO the map rather than a disc under a floating
 * one.
 */
.sf-home-live__pin::before {
  /*
   * THE BOARD'S STOPS, not the demo's. The demo runs
   * `…0.14 72%, rgba(23,24,26,0.05) 100%` and HomeB draws
   * `…0.12 72%, transparent 100%` — a lighter, cleaner tail. The board wins
   * (playbook §3), and the difference is not theoretical: it read 3.214% on
   * this region's own crop.
   */
  background: radial-gradient(
    ellipse at 82% 50%,
    rgba(23, 24, 26, 0.5) 0%,
    rgba(23, 24, 26, 0.3) 45%,
    rgba(23, 24, 26, 0.12) 72%,
    transparent 100%
  );
  /*
   * -8, and the demo says -6. Measured off all six of the board's drawn
   * shadows: each one's bottom edge sits EIGHT pixels below its pin's box, at
   * both pin sizes. Board wins again.
   */
  bottom: -8px;
  clip-path: polygon(
    100% 46%,
    100% 54%,
    42% 86%,
    24% 100%,
    9% 95%,
    0% 72%,
    0% 28%,
    9% 5%,
    24% 0%,
    42% 14%
  );
  content: "";
  filter: blur(2.5px);
  height: 15px;
  left: 50%;
  margin-left: -36px;
  position: absolute;
  transform: rotate(32deg);
  transform-origin: right center;
  width: 38px;
  z-index: -2;
}

/* An order that was already on the map when the reader arrived: smaller, and
 * quieter. Board values, including the 0.72. */
.sf-home-live__pin--aged {
  height: 26px;
  margin: -26px 0 0 -10px;
  width: 20px;
}

/*
 * THE 0.72 IS ON THE PIN BODY, NOT ON THE PIN.
 *
 * The demo dims the whole `.mpin`, which takes its ::before cast shadow down
 * with it. The BOARD draws the cast shadow as a SIBLING of the pin div, with no
 * opacity on it at all — so on the board an aged pin is faded and its shadow is
 * full strength. Board wins (playbook §3), and it is the entire threshold-2
 * residual this region had: a smooth 3-to-24 decay across the whole flare.
 */
.sf-home-live__pin--aged svg {
  opacity: 0.72;
}

/*
 * The aged pin's shadow is NARROWER and exactly as TALL. The demo shrinks it to
 * 11px; all four of the board's aged shadows are drawn 27 x 15, the same height
 * as the full-size pair — so only the width and the offset change here.
 */
.sf-home-live__pin--aged::before {
  margin-left: -25px;
  width: 27px;
}

/*
 * THE DART DROP. 0.42s, and NO BOUNCE: the pin never travels back up. The 90%
 * keyframe is a SQUASH — 1.04 wide by 0.9 tall at a fixed `translateY(0)` — the
 * impact deforming the teardrop, not rebounding it.
 *
 * Transform and opacity only, so it composites and never touches layout. The
 * duration is mirrored in `home-live-orders-data.ts` as `DROP_MS`, and
 * `tests/home-live-orders.test.ts` reads this rule and holds the two together:
 * a driver that thinks the drop is shorter than the sheet does fires the
 * shockwave in mid-air.
 */
.sf-home-live__pin--dropping {
  animation: sf-home-live-drop 0.42s cubic-bezier(0.55, 0, 0.85, 0.55) both;
}

@keyframes sf-home-live-drop {
  0% {
    opacity: 0;
    transform: translateY(-200px) scale(1.06);
  }

  12% {
    opacity: 1;
  }

  82% {
    transform: translateY(0) scale(1);
  }

  90% {
    transform: translateY(0) scale(1.04, 0.9);
  }

  100% {
    transform: translateY(0) scale(1);
  }
}

/*
 * THE GROUND-PLANE HALO SHOCKWAVE. A ring at the pin's point that expands and
 * fades ONCE, flattened to 0.45 on the vertical axis so it reads as a wave
 * travelling across the map's plane rather than a bubble in the air, and drawn
 * BEHIND the pin body (`z-index: -1` against the pin's own stacking).
 *
 * `1 both` and a class applied exactly once by the driver: this fires on the
 * frame the pin lands and never again.
 */
.sf-home-live__pin--landed::after {
  animation: sf-home-live-shock 0.5s ease-out 1 both;
  border: 2.5px solid var(--brand-color-accent-on-dark, #ffb27a);
  border-radius: var(--radius-pill);
  bottom: -4px;
  box-shadow:
    0 0 0 1.5px rgba(196, 83, 10, 0.85),
    0 0 14px rgba(255, 178, 122, 0.9);
  content: "";
  height: 18px;
  left: 50%;
  margin-left: -9px;
  position: absolute;
  width: 18px;
  z-index: -1;
}

@keyframes sf-home-live-shock {
  0% {
    opacity: 1;
    transform: scale(0.25) scaleY(0.45);
  }

  100% {
    opacity: 0;
    transform: scale(3.6) scaleY(0.45);
  }
}

/*
 * THE ORDER CARD, at the pin. Board-verbatim: white, a 1px ACCENT border — the
 * only card on the site that takes one, because it is a live event rather than
 * a surface — and a soft drop shadow. `translate(-50%, -100%)` puts its bottom
 * centre on the coordinate the driver hands it.
 */
.sf-home-live__card {
  background: var(--brand-color-surface, #ffffff);
  border: 1px solid var(--brand-color-accent, #c4530a);
  border-radius: var(--radius-control);
  box-shadow: 0 10px 26px rgba(23, 24, 26, 0.16);
  color: var(--brand-color-ink, #17181a);
  font-size: var(--text-small);
  padding: 9px 14px;
  position: absolute;
  /*
   * CENTRED ON THE POINT AND LIFTED OFF IT — with two corrections the driver
   * supplies when the band's edge is in the way (see `useCardEdge`). Both are
   * ZERO at the board's own geometry, so this resolves to the board's plain
   * `translate(-50%, -100%)` on every card the board draws; the fallbacks are
   * what a card renders at before the layout effect has measured it.
   */
  transform: translate(
    calc(-50% + var(--sf-home-live-card-shift, 0px)),
    var(--sf-home-live-card-lift, -100%)
  );
  transition: opacity 0.9s;
  white-space: nowrap;
  z-index: 4;
}

.sf-home-live__card span {
  color: var(--brand-color-muted, #6c6a62);
  display: block;
  font-size: var(--text-caption);
  margin-top: 1px;
}

.sf-home-live__card--fading,
.sf-home-live__pin--fading {
  opacity: 0;
}

/*
 * REDUCED MOTION. The pins still arrive and the cards still say who bought
 * what — the feed IS the content — but nothing darts, nothing pulses, nothing
 * fades. The driver already skips the `--dropping` and `--landed` classes at
 * this preference; these rules are the belt to that braces, and they also catch
 * the chip's pulse, which no driver touches.
 */
@media (prefers-reduced-motion: reduce) {
  /* The ring and the halo LEAVE rather than merely stopping: an animation-less
   * ring sits at its natural full size, which is a second static circle nobody
   * asked for, and a halted shockwave is a hoop around every pin. */
  .sf-home-live__dot::after,
  .sf-home-live__pin--landed::after {
    display: none;
  }

  .sf-home-live__pin--dropping {
    animation: none;
  }

  .sf-home-live__card,
  .sf-home-live__pin {
    transition: none;
  }
}

@media (max-width: 767.98px) {
  .sf-home-live {
    margin-top: var(--space-section-mobile);
  }

  /*
   * The indent follows the SMALLER circle: 34 of circle plus the mobile
   * header's 11px gap. No board draws the mobile sub line — the mobile boards
   * for this page do not exist — so it is derived from the header beside it
   * rather than invented, and FLAGGED as derived.
   */
  .sf-home-live__sub {
    font-size: var(--text-support);
    margin-left: 45px;
  }

  /* The demo's own mobile band. The map is a backdrop on a phone, not a
   * surface to read — 300px is what it draws. */
  .sf-home-live__bleed {
    /* 300 of white plus the two hairlines — see the desktop rule. */
    height: 302px;
  }

  .sf-home-live__card {
    font-size: var(--text-caption);
  }
}
