/* ==========================================================================
   HOMEPAGE HERO, ROUND 6 (2026-11-04): THE OPEN TURNTABLE AND THE GLASS GROUND
   Loaded on the front page and its language copies only (functions.php), after
   sections.css. Everything here is scoped to .hero--stage, a class only the new
   render.php prints, so no other hero (chemistry, industry, page) is touched.

   Leonard's direction for this round: the product must not sit in a box or a card.
   It stands free on the hero itself, lit, with its shadow and reflection, and its
   name, chemistry and controls sit loose beside and under it. The frosted glass is
   not a frame either: it is one full-width layer between the drifting red glow
   (behind it) and everything the visitor reads (in front of it).

   Leonard's round 4 review of this stage (same day), and what changed for it:
     - Desktop and landscape tablet: the headline carries the hero. Its column is wider
       and its type larger; the product column is narrower and sits further right; the
       product's caption (chemistry, name, View product) is smaller and centred under
       the product with the controls under it, so product, caption and controls read as
       one unit. A caption bar beside the controls was tried and lost (HERO-REPORT.md).
     - Phones (up to 600px wide, and phones held sideways: up to 1024px wide and 500px
       tall): no product stage at all, and no glass fold-open. The headline, the intro and
       the buttons sit centred in one screen above the ticker, with the red glow drifting
       low behind the buttons. render.php gives phones a 1px blank in place of every
       product photo, so they download none.

   What this file does, in order:
     1. The ground: mesh bleed in px, the red glow that visibly drifts behind the glass.
     2. The grid: copy left / stage right from 900px, stacked below, no stage on phones.
     3. The copy: headline size from its own column (cqi), more air between the
        headline, the sub line and the buttons. Markup and words are untouched.
     4. The glass: the full-width frosted layer, and the fold-open that plays once
        (two leaves swing open from a lit hinge, then one sweep of light).
     5. The stage: a lit turntable with one product at a time, its caption under it
        (beside it only on a portrait tablet), then the controls.
     6. Idle motion, reduced motion, and the off-screen pause.

   Performance rules this file keeps (Leonard's brief): every inset is a px value
   (a percentage inset tied a layer to the hero's height and cost CLS 0.29 before);
   every animation and transition moves only transform or opacity; exactly ONE large
   backdrop-filter (.hero-glass__pane); everything decorative pauses off screen
   (.is-offscreen, set by view.js) and stops under prefers-reduced-motion.

   Specificity: the three must-use plugins (mightyloc-mobile-hero-cards, -layout,
   -typography) print <style> blocks AFTER this file, so equal specificity loses to
   them. Rules that must beat a plugin rule carry one class more than it does; each
   one says which rule it beats.

   REVERT: delete this file and its enqueue in functions.php, and copy back
   blocks/hero/{render.php,view.js,block.json,view.asset.php} from the backup.
   ========================================================================== */

.hero.hero--stage {
  --hs-ease: cubic-bezier(0.16, 1, 0.3, 1);
  /* The stage's square deck, as a cap per layout. Phones and tablets: fixed caps.
     From 1025px the hero is one viewport tall, so the caps follow the height (below). */
  --hs-scene-narrow: 400px;
  --hs-scene-wide: 460px;
}

/* ---------- 1. THE GROUND ---------- */

/* The mesh bled by inset:-10%, a percentage of the hero's own height. Same bleed in px,
   so the layer never moves when the hero's height changes. Beats the phone plugin's
   `body .hero-mesh::before { inset:-48px }` (0,1,2) at (0,3,1). */
.hero.hero--stage.hero-mesh::before { inset: -120px; }

/* THE RED GLOW. It sat 16 percent wide and roamed 8 percent of its own width, which
   nobody could see move. Leonard asked for a glow that visibly drifts behind the
   glass: a 560px source behind the product that travels up to about 190px in a 22 s
   loop. Top is px (no percentage of the hero height); the horizontal position is a
   share of the viewport width, which content can never change.
   Beats the plugin's `body .hero.hero-mesh .hero__redglow { top:420px }` (0,3,1). */
body .hero.hero--stage.hero-mesh .hero__redglow {
  width: 560px; height: 560px; aspect-ratio: auto;
  /* round 4: 80px further right with the product (centred behind it at 1280 to 1920) */
  left: calc(50% + 120px); top: 150px;
  background: radial-gradient(circle,
    rgba(240, 52, 54, 0.92) 0%,
    rgba(208, 38, 41, 0.55) 30%,
    rgba(208, 38, 41, 0.18) 52%,
    rgba(208, 38, 41, 0) 70%);
  filter: blur(48px);
  opacity: 0.85;
  animation: hsGlowDrift 22s ease-in-out infinite;
}
@keyframes hsGlowDrift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  24%  { transform: translate3d(-190px, 80px, 0) scale(1.12); }
  50%  { transform: translate3d(-60px, 190px, 0) scale(0.92); }
  76%  { transform: translate3d(130px, 70px, 0) scale(1.08); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}
@media (max-width: 1024px) {
  body .hero.hero--stage.hero-mesh .hero__redglow { width: 420px; height: 420px; left: calc(50% - 60px); top: 560px; }
}
@media (min-width: 900px) and (max-width: 1024px) and (min-height: 501px) {
  body .hero.hero--stage.hero-mesh .hero__redglow { left: calc(50% + 90px); top: 200px; }
}
/* PHONES: no product, so the glow becomes a low light under the buttons, between them and
   the ticker, and drifts sideways along that band (a smaller loop than the desktop one,
   which would carry it under the ticker half the time). Portrait: the hero is one screen
   tall (section 2), so its bottom edge is the screen's (svh; vh where svh is unknown).
   Sideways: the hero is as tall as its copy, and the glow sits behind the lower copy. */
@media (max-width: 600px) {
  body .hero.hero--stage.hero-mesh .hero__redglow {
    width: 380px; height: 380px; left: calc(50% - 190px);
    top: calc(100vh - 280px); top: calc(100svh - 280px);
    animation-name: hsGlowDriftLow;
  }
}
@media (min-width: 601px) and (max-width: 1024px) and (max-height: 500px) {
  body .hero.hero--stage.hero-mesh .hero__redglow {
    width: 380px; height: 380px; left: calc(50% - 190px); top: 250px;
    animation-name: hsGlowDriftLow;
  }
}
@keyframes hsGlowDriftLow {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  25%  { transform: translate3d(-120px, -30px, 0) scale(1.1); }
  50%  { transform: translate3d(-20px, 24px, 0) scale(0.94); }
  75%  { transform: translate3d(110px, -18px, 0) scale(1.06); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}

/* ---------- 2. THE GRID ---------- */

/* Stacked (phones and tablets): copy, then the stage. The deck's top is open air above
   the product, so the gap between the buttons and the stage stays small. Beats the
   phone plugin's `.hero .hero__inner { gap:24px; padding-bottom:28px }` (0,2,0). */
.hero.hero--stage .hero__inner {
  row-gap: 16px;
  padding-bottom: 36px;
}
.hero.hero--stage .hero__copy,
.hero.hero--stage .hero-stage { position: relative; z-index: 1; }
.hero.hero--stage .hero-stage {
  width: min(100%, 600px);
  justify-self: center;
  align-self: center;
  container: hstage / inline-size;
}

/* From 900px: copy on the left, left-aligned again, the stage on the right. The tablet
   plugin centres the copy for the old stacked tablet hero; these beat its
   `body .hero .hero__copy...` rules (0,2,1) at (0,3,1). A phone held sideways (up to
   1024px wide and 500px tall) keeps the plugin's centred copy in one column instead:
   it shows no stage (below). */
@media (min-width: 1025px), (min-width: 900px) and (min-height: 501px) {
  .hero.hero--stage .hero__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 48px;
    padding-top: calc(var(--header-bottom) + 28px);
    padding-bottom: 40px;
  }
  .hero.hero--stage .hero-stage { width: 100%; }
  .hero.hero--stage { --hs-scene-narrow: 360px; --hs-scene-wide: 420px; }
}
/* 900 to 1024 (a landscape tablet, 1024x768): round 4 gives the headline the larger share,
   a stage column of 36vw between 320 and 380px, so the copy is about 550px wide. */
@media (min-width: 900px) and (max-width: 1024px) and (min-height: 501px) {
  .hero.hero--stage .hero__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, clamp(320px, 36vw, 380px));
    column-gap: 40px;
  }
  body .hero.hero--stage .hero__copy { text-align: left; }
  body .hero.hero--stage .hero__copy > * { margin-left: 0; margin-right: 0; }
  body .hero.hero--stage .hero__cta { justify-content: flex-start; }
  /* The stage stacks product, caption and controls in a column, so the deck follows the
     viewport height. 290px = the top air, the caption, the controls and the 90px the
     site's floating "Ask MightyBot" button takes at the bottom right of the screen,
     measured at 1024x768: the "next" arrow ends clear above that button. */
  .hero.hero--stage { --hs-scene-narrow: clamp(240px, calc((100vh - var(--header-bottom) - 290px) / 1.14), 360px); }
}

/* 1025 and up: the hero is the viewport (sections.css round 5). Round 4: the stage column
   is 34vw + 24px between 400 and 520px (it was 45vw + 24px, 484 to 664px), so the headline
   column gains about 150px and the product sits further right. The top padding is the
   floating header's own bottom edge plus 24px: the deck's upper part is open air, so the
   product itself starts well clear of the header. */
@media (min-width: 1025px) {
  .hero.hero--stage .hero__inner {
    grid-template-columns: minmax(0, 1fr) minmax(0, clamp(400px, calc(34vw + 24px), 520px));
    column-gap: clamp(40px, 4.4vw, 72px);
    min-height: calc(100vh - var(--ticker-h));
    padding-top: calc(var(--header-bottom) + 24px);
    padding-bottom: 32px;
  }
  .hero.hero--stage {
    /* the row the stage lives in: viewport, less ticker, header and both paddings */
    --hs-row: calc(100vh - var(--ticker-h) - var(--header-bottom) - 56px);
    /* deck cap: the caption and the controls sit under the product (section 5) and take
       160px of the row with their gaps (a one-line name: 10 + 81 + 10 + 6 + 48, plus 5) */
    --hs-scene-wide: clamp(200px, calc((var(--hs-row) - 160px) / 1.14), 500px);
  }
}
/* A short, wide laptop screen (under 720px of height and at least 1.7 times as wide as
   tall, e.g. a 1366x768 laptop, whose browser shows about 1366x650): the caption moves
   beside the product (section 5), so only the controls share the height, and the product
   stays about 40 percent larger (1366x650: a 284px deck instead of 205px). On a narrower
   short screen (1100x700) the column is too narrow for that and the caption stays under. */
@media (min-width: 1025px) and (max-height: 719px) and (min-aspect-ratio: 17/10) {
  .hero.hero--stage { --hs-scene-wide: clamp(180px, calc((var(--hs-row) - 70px) / 1.14), 500px); }
}

/* PHONES (Leonard, round 4): no product on the hero. Phones are the site's phone tier (up
   to 600px wide, the split sections.css and the mobile plugins use) and phones held
   sideways (up to 1024px wide and 500px tall). The whole stage goes: turntable, caption,
   arrows, counter. render.php hands these screens a 1px blank for every product photo.
   One column, so a sideways phone 900px or wider does not keep sections.css's empty
   second column (1.6fr 0.8fr from 900px) beside the copy. */
@media (max-width: 600px), (max-width: 1024px) and (max-height: 500px) {
  .hero.hero--stage .hero-stage { display: none; }
  .hero.hero--stage .hero__inner { grid-template-columns: minmax(0, 1fr); }
}
/* A portrait phone: the hero is one screen again, the copy centred in it above the ticker
   (sections.css run 25 did this for phones and the hero cards plugin's `min-height: 0`
   undid it for the old product cards). Beats `.hero .hero__inner` (0,2,0) at (0,3,0). Under
   600px of height the copy fills the screen anyway, so nothing is forced there. */
@media (max-width: 600px) and (min-height: 600px) {
  .hero.hero--stage .hero__inner {
    min-height: calc(100vh - var(--ticker-h));
    min-height: calc(100svh - var(--ticker-h));
  }
  .hero.hero--stage .hero__copy { justify-content: center; }
}

/* ---------- 3. THE COPY ---------- */

/* The copy column is a size container, so the headline can be sized from the column
   it actually has. The H1 element, its words and its accent span are unchanged. */
.hero.hero--stage .hero__copy { container: hcopy / inline-size; min-width: 0; }

@media (min-width: 900px) {
  .hero.hero--stage .hero__h1 { margin-bottom: 26px; }
  .hero.hero--stage .hero__sub { margin-bottom: 36px; }
  .hero.hero--stage .hero__cta { gap: 14px; }
}
/* THE HEADLINE SIZE from 900px is the smaller of two limits:
     - the column: 14.2cqi keeps the longest English line ("Southeast Asia.", 6.7em)
       inside the copy column;
     - the height: the screen height less everything else in the hero, shared by the
       headline's lines. --h1-em is that share in em, per language: English and Chinese
       set in 4 lines at line-height 0.96 (3.85em); Malay, Indonesian, Tagalog,
       Vietnamese and Thai need up to 5 (4.85em). Up to 1024px the typography plugin sets
       taller lines for Vietnamese (1.1), Thai and Chinese (1.2), so their share grows.
   Measured in all seven languages: none pushes the ticker below the screen. */
.hero.hero--stage .hero__h1 { --h1-em: 3.85; }
.hero.hero--stage .hero__h1:is(:lang(ms), :lang(id), :lang(fil), :lang(vi), :lang(th)) { --h1-em: 4.85; }
@media (max-width: 1024px) {
  .hero.hero--stage .hero__h1:lang(vi) { --h1-em: 5.55; }
  .hero.hero--stage .hero__h1:lang(th) { --h1-em: 6.05; }
  .hero.hero--stage .hero__h1:lang(zh) { --h1-em: 3.65; }
}
/* 1025 and up. The old tiers (8.4vw, 1025-1119, max-height 820 and 1000) are replaced
   here at (0,3,0). Round 4: the wider column and the height rule make the English
   headline about 20 percent larger (1440x900: 87 to 108px; 1366x768: 75 to 89px), up
   to 112px. 434px = header and its gap 170, ticker 54, bottom gap 32, the rest of the
   copy (gaps 26 and 36, a two-line intro 61, the buttons 48) 171, and 7 to spare; on
   screens up to 820px tall the gaps are 22 and 30, so 424px. */
@media (min-width: 1025px) {
  .hero.hero--stage .hero__h1 {
    font-size: clamp(48px, min(14.2cqi, calc((100vh - 434px) / var(--h1-em, 3.85))), 112px);
  }
}
@media (min-width: 1025px) and (max-height: 820px) {
  .hero.hero--stage .hero__h1 {
    margin-bottom: 22px;
    font-size: clamp(48px, min(14.2cqi, calc((100vh - 424px) / var(--h1-em, 3.85))), 112px);
  }
  .hero.hero--stage .hero__sub { margin-bottom: 30px; }
}
/* Tagalog's intro needs a third line in a copy column under about 580px (screens 1025 to
   1099px wide), so its headline gets a larger share there. */
@media (min-width: 1025px) and (max-width: 1099px) {
  .hero.hero--stage .hero__h1:lang(fil) { --h1-em: 5.25; }
}
/* English keeps its four-line shape at every size from 900px ("Engineered / in Singapore.
   / Bonded across / Southeast Asia."): the line is at most 7.05em, the width of the
   longest of the four. In a wide, short column the headline would otherwise fall into
   three lines that break mid-sentence ("Singapore. Bonded"). The height rule above is
   measured for four lines. Other languages keep the column's width. */
@media (min-width: 900px) {
  .hero.hero--stage .hero__h1:lang(en) { max-width: 7.05em; }
}
/* 900 to 1024 (landscape tablet): the same rule for the wider copy column, up to 84px
   (1024x768: 77px in English, it was the site's 50px). 470px = header and its gap 174,
   bottom gap 40, ticker 54, and the rest of the copy with a three-line intro 202. */
@media (min-width: 900px) and (max-width: 1024px) and (min-height: 501px) {
  .hero.hero--stage .hero__h1 {
    font-size: clamp(46px, min(14.2cqi, calc((100vh - 470px) / var(--h1-em, 3.85))), 84px);
  }
}
/* Phones: with no product beside it, the headline takes its full column: 14.2cqi again
   (390px wide: 46px; 430px: 52px), up from the hero cards plugin's fixed 45px, which it
   beats ((0,2,1)) at (0,4,0). Only the gaps open up a little. */
@media (max-width: 600px) {
  .hero.hero--stage .hero__copy .hero__h1 { font-size: clamp(38px, 14.2cqi, 60px); }
  .hero.hero--stage .hero__h1 { margin-bottom: 22px; }
  .hero.hero--stage .hero__sub { margin-bottom: 30px; }
}

/* ---------- 4. THE GLASS ---------- */

/* One frosted layer across the whole hero above the ticker, edge to edge of the window.
   It sits inside .hero__inner, so its top and bottom ARE the hero's top and the ticker's
   top (px, no percentage of a height), and reaches past the 1400px content box to the
   window edges on wide screens. The hero clips it (overflow: clip). Only __pane is
   blurred glass (the one large backdrop-filter); the leaves, the hinge light and the
   sweep are light drawn without any filter, and play the fold-open once. */
.hero.hero--stage .hero__inner > .hero-glass {
  position: absolute; z-index: 0;
  top: 0; bottom: 0;
  left: min(0px, calc(700px - 50vw));
  right: min(0px, calc(700px - 50vw));
  pointer-events: none;
}
.hero-glass__pane {
  position: absolute; inset: 0;
  overflow: hidden;
  /* Kept dim on the headline side: brighter tint or more colour boost lifts the blue behind
     the white text and the red "Bonded" below the live site's contrast (measured with the
     text hidden: this setting matches live, 0.075 / 150% did not). */
  background:
    linear-gradient(112deg,
      rgba(255, 255, 255, 0.04) 0%,
      rgba(255, 255, 255, 0.02) 36%,
      rgba(255, 255, 255, 0.015) 62%,
      rgba(255, 255, 255, 0.055) 100%);
  -webkit-backdrop-filter: blur(24px) saturate(115%);
  backdrop-filter: blur(24px) saturate(115%);
}
/* Frost: a fine grain over the glass, one small SVG noise tile, painted once. */
.hero-glass__pane::after {
  content: ''; position: absolute; inset: 0;
  opacity: 0.06;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .55 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* One band of light that crosses the glass when it opens.
   Round 4: the band is the ::before of a host that covers the whole glass. The site's
   pause-animations module (interactions.js) pauses the animations of any element outside
   the window, and the band itself waits off the glass's left edge, so as its own element
   it was paused before it ever started: it never crossed, and the fold-open never
   finished (measured: hgSweep "paused" 8 s after the curtain). The module watches the
   host, which is always in view while the hero is, so the band now runs; off screen the
   hero's own rule in section 6 pauses it. Same band, same size, same path. */
.hero-glass__sweep {
  position: absolute; top: -80px; bottom: -80px; left: 0; right: 0;
}
.hero-glass__sweep::before {
  content: '';
  position: absolute; top: 0; bottom: 0; left: 0; width: 46%;
  background: linear-gradient(102deg,
    rgba(255, 255, 255, 0) 20%,
    rgba(255, 255, 255, 0.13) 46%,
    rgba(255, 255, 255, 0.04) 56%,
    rgba(255, 255, 255, 0) 76%);
  transform: translateX(-110%);
}
/* The two leaves: the left and right halves of the glass, hinged on its centre line. */
.hero-glass__leaf {
  position: absolute; top: 0; bottom: 0; width: 50%;
  opacity: 0;
  background: linear-gradient(160deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.05) 55%, rgba(255, 255, 255, 0.09));
  border: 1px solid rgba(255, 255, 255, 0.36);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
}
.hero-glass__leaf--l { left: 0; border-right: 0; transform-origin: 100% 50%; }
.hero-glass__leaf--r { right: 0; border-left: 0; transform-origin: 0 50%; }
/* The hinge: a line of light down the fold that grows, then fades as the leaves open. */
.hero-glass__bead {
  position: absolute; left: 50%; top: 48px; bottom: 48px; width: 2px; margin-left: -1px;
  opacity: 0;
  background: linear-gradient(to bottom,
    rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.95) 16%, #ff6a55 50%, rgba(255, 255, 255, 0.95) 84%, rgba(255, 255, 255, 0));
  box-shadow: 0 0 24px 6px rgba(255, 84, 64, 0.5);
  transform-origin: 50% 50%;
}

/* THE FOLD-OPEN. With scripting on, the glass waits folded and view.js adds .is-open
   to the hero once the first-visit curtain has lifted. If view.js never runs, the same
   animation starts by itself after 5 s (the *Late keyframe names), so the glass can
   never stay hidden. The names differ so .is-open restarts the motion from the top.
   Without scripting, or under reduced motion, none of this matches and the glass is
   simply there, flat. Only glass moves: the copy and the product are in front of it
   and never hidden.
   Round 4: phones (the same two phone sizes as section 2) never fold open; Leonard asked
   for no opening effect there. And once the fold-open has played, or did not play,
   view.js adds .is-settled, which these rules exclude, so a resize or a rotation into a
   size that has the fold-open can never replay it. */
/* 2026-11-04 (Leonard): the fold-open is REMOVED on every screen. "I don't want that animation to
   happen every time I refresh, with this look of something opening from the middle." The rules
   that played it lived here; without them the glass is simply there, flat, on every load
   (the leaves, hinge light and sweep stay invisible) and view.js marks the hero settled at once.
   The red glow keeps drifting. REVERT: copy round4-backup\hero-stage.css from
   F:\agent-work\mightyloc\menu-hero-2026-11-04 back over this file. */
@keyframes hgPane      { from { opacity: 0; } to { opacity: 1; } }
@keyframes hgPaneLate  { from { opacity: 0; } to { opacity: 1; } }
@keyframes hgLeafL {
  0%   { opacity: 1; transform: perspective(1800px) rotateY(-86deg); }
  70%  { opacity: 1; transform: perspective(1800px) rotateY(0deg); }
  100% { opacity: 0; transform: perspective(1800px) rotateY(0deg); }
}
@keyframes hgLeafLLate {
  0%   { opacity: 1; transform: perspective(1800px) rotateY(-86deg); }
  70%  { opacity: 1; transform: perspective(1800px) rotateY(0deg); }
  100% { opacity: 0; transform: perspective(1800px) rotateY(0deg); }
}
@keyframes hgLeafR {
  0%   { opacity: 1; transform: perspective(1800px) rotateY(86deg); }
  70%  { opacity: 1; transform: perspective(1800px) rotateY(0deg); }
  100% { opacity: 0; transform: perspective(1800px) rotateY(0deg); }
}
@keyframes hgLeafRLate {
  0%   { opacity: 1; transform: perspective(1800px) rotateY(86deg); }
  70%  { opacity: 1; transform: perspective(1800px) rotateY(0deg); }
  100% { opacity: 0; transform: perspective(1800px) rotateY(0deg); }
}
@keyframes hgBead {
  0%   { opacity: 0; transform: scaleY(0); }
  18%  { opacity: 1; transform: scaleY(1); }
  55%  { opacity: 0.9; transform: scaleY(1); }
  100% { opacity: 0; transform: scaleY(1); }
}
@keyframes hgBeadLate {
  0%   { opacity: 0; transform: scaleY(0); }
  18%  { opacity: 1; transform: scaleY(1); }
  55%  { opacity: 0.9; transform: scaleY(1); }
  100% { opacity: 0; transform: scaleY(1); }
}
@keyframes hgSweep     { from { transform: translateX(-110%); } to { transform: translateX(240%); } }
@keyframes hgSweepLate { from { transform: translateX(-110%); } to { transform: translateX(240%); } }

/* ---------- 5. THE STAGE ---------- */

.hero-stage__frame {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "scene" "info" "nav";
  justify-items: center;
  row-gap: 12px;
  text-align: center;
}

/* The scene: a square deck plus a floor band under it (0.14 of the width) for the
   turntable ring, the contact shadow and the reflection. No background, no edge. */
.hero-stage__scene {
  grid-area: scene;
  position: relative;
  width: min(100%, var(--hs-scene-narrow));
  aspect-ratio: 1 / 1.14;
  touch-action: pan-y;
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
}
/* The deck: the square zone every product is sized in (render.php $zone_k = 1). It
   leans a few degrees toward a desktop pointer (--rx / --ry from view.js). */
.hero-stage__deck {
  position: absolute; left: 0; top: 0; width: 100%; aspect-ratio: 1 / 1;
  transform: perspective(1600px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform 900ms var(--hs-ease);
}
/* The product's own light: a soft pool on the glass behind it, brightest just above the
   turntable, fading to nothing well before any edge, so the product reads as lit on the
   hero rather than placed in a panel. Static. */
.hero-stage__deck::before {
  content: ''; position: absolute; left: -10%; right: -10%; top: 2%; bottom: -14%;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    rgba(255, 255, 255, 0.13),
    rgba(255, 236, 240, 0.055) 46%,
    rgba(255, 255, 255, 0) 100%);
  pointer-events: none;
}

/* THE TURNTABLE. The floor line is the deck's bottom edge; a tilted circle centred on
   it reads as a disc the product stands on. Its far left and right fade out, so the disc
   melts into the hero instead of drawing an outline. A slow turn of its tick marks (and
   one red index mark) is its only idle motion. */
.hero-stage__floor {
  position: absolute; left: 3%; right: 3%; top: 100%;
  aspect-ratio: 1 / 0.26;
  transform: translateY(-50%);
  pointer-events: none;
  -webkit-mask: linear-gradient(90deg, transparent 0%, #000 22%, #000 78%, transparent 100%);
  mask: linear-gradient(90deg, transparent 0%, #000 22%, #000 78%, transparent 100%);
}
.hero-stage__floor::before {
  content: ''; position: absolute; inset: -18% -6%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0.05) 55%, rgba(255, 255, 255, 0) 100%);
}
.hero-stage__floor::after {
  content: ''; position: absolute; inset: 0;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.14);
}
.hero-stage__ring {
  position: absolute; left: 4%; width: 92%; top: 50%; aspect-ratio: 1 / 1;
  border-radius: 50%;
  -webkit-mask: radial-gradient(closest-side, transparent 86%, #000 87%, #000 96%, transparent 97%);
  mask: radial-gradient(closest-side, transparent 86%, #000 87%, #000 96%, transparent 97%);
  transform: translateY(-50%) perspective(1400px) rotateX(75deg) rotate(0deg);
  animation: hsRing 60s linear infinite;
}
/* The marks themselves, on a child so the third-of-a-turn spin on every product change
   (view.js spinRing) can turn them inside the tilted plane without touching the tilt. */
.hero-stage__ring i {
  position: absolute; inset: 0;
  border-radius: 50%;
  background:
    conic-gradient(from -4deg, rgba(240, 52, 54, 0.95) 0deg 8deg, rgba(240, 52, 54, 0) 8deg 360deg),
    repeating-conic-gradient(rgba(255, 255, 255, 0.40) 0deg 0.6deg, rgba(255, 255, 255, 0) 0.6deg 6deg);
}
@keyframes hsRing {
  from { transform: translateY(-50%) perspective(1400px) rotateX(75deg) rotate(0deg); }
  to   { transform: translateY(-50%) perspective(1400px) rotateX(75deg) rotate(360deg); }
}

/* ONE FIGURE PER PRODUCT, all in the same place. Only the product on stage, its two
   neighbours and the one turning away are displayed (state.isNear), so a lazy photo
   loads when it is one press away and never before; only the active one is visible.
   The figure IS the product's visible box (--pw / --ph from render.php), standing on
   the floor line and centred; the photo hangs its transparent margins outside it. */
.hero-stage__product {
  position: absolute; margin: 0;
  bottom: 0; left: calc(50% - var(--pw, 54%) / 2);
  width: var(--pw, 54%); height: var(--ph, 62%);
  display: none; visibility: hidden;
}
.hero-stage__product.is-near { display: block; }
.hero-stage__product.is-active { visibility: visible; }

.hero-stage__turn,
.hero-stage__sway,
.hero-stage__float { position: absolute; inset: 0; transform-origin: 50% 55%; }

.hero-stage__img {
  position: absolute;
  left: var(--ix, 0%); top: var(--iy, 0%);
  width: var(--iw, 100%); height: var(--ih, 100%);
  max-width: none; object-fit: fill;
  pointer-events: none;
  -webkit-user-drag: none;
}

/* A highlight that glides across the product as it sways, and passes once, brighter,
   when a product turns in (view.js). Masked by the photo's own alpha, which view.js
   hands over as --img (the file the <img> already downloaded). Off until it has. */
.hero-stage__sheen {
  position: absolute;
  left: var(--ix, 0%); top: var(--iy, 0%);
  width: var(--iw, 100%); height: var(--ih, 100%);
  overflow: hidden; pointer-events: none;
  opacity: var(--lit, 0);
  -webkit-mask: var(--img) 0 0 / 100% 100% no-repeat;
  mask: var(--img) 0 0 / 100% 100% no-repeat;
}
.hero-stage__sheen i {
  position: absolute; top: 0; bottom: 0; left: -50%; width: 200%;
  background: linear-gradient(104deg,
    rgba(255, 255, 255, 0) 36%,
    rgba(255, 255, 255, 0.26) 47%,
    rgba(255, 255, 255, 0.07) 53%,
    rgba(255, 255, 255, 0) 62%);
  transform: translateX(-30%);
}
/* The reflection in the glass floor: the same photo, mirrored about the floor line and
   faded out within a short distance. */
.hero-stage__reflect {
  position: absolute; left: 0; right: 0; top: 100%; height: 100%;
  overflow: hidden; pointer-events: none;
  opacity: var(--lit, 0);
  -webkit-mask: linear-gradient(to bottom, rgba(0, 0, 0, 0.30), rgba(0, 0, 0, 0) 20%);
  mask: linear-gradient(to bottom, rgba(0, 0, 0, 0.30), rgba(0, 0, 0, 0) 20%);
}
.hero-stage__reflect i {
  position: absolute;
  left: var(--ix, 0%);
  top: calc(100% - var(--iy, 0%) - var(--ih, 100%));
  width: var(--iw, 100%); height: var(--ih, 100%);
  background: var(--img) 0 0 / 100% 100% no-repeat;
  transform: scaleY(-1);
}
/* The contact shadow on the floor, under the product. */
.hero-stage__shadow {
  position: absolute; left: -4%; right: -4%; bottom: -3.5%; height: 7%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(4, 3, 22, 0.78), rgba(4, 3, 22, 0.36) 55%, rgba(4, 3, 22, 0));
  pointer-events: none;
}

/* A photo whose size data is missing: its code stands in, as type on the turntable,
   with no plate or box behind it. (Products with no photo at all are not in the pool.) */
.hero-stage__plate {
  position: absolute; inset: 0;
  display: flex; align-items: flex-end; justify-content: center;
  padding-bottom: 12%;
}
.hero-stage__plate-code {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(26px, 9cqi, 46px); line-height: 1; letter-spacing: -0.03em;
  color: rgba(255, 255, 255, 0.92); text-align: center; overflow-wrap: anywhere;
  text-shadow: 0 8px 30px rgba(4, 3, 22, 0.6);
}

/* NAME AND CHEMISTRY, loose on the hero. One block per product stacked in one grid
   cell, so the cell is as tall as the longest name and never changes height.
   Round 4 (Leonard: the words by the product a bit smaller): a caption, not a second
   headline. The name is 20px (it was 20 to 26px), View product 14px with a 28px arrow
   (15px and 34px), and the gaps between the three lines are tighter. */
.hero-stage__infos { grid-area: info; display: grid; width: 100%; min-width: 0; }
.hero-stage__info { grid-area: 1 / 1; min-width: 0; visibility: hidden; opacity: 0; }
.hero-stage__info.is-active { visibility: visible; opacity: 1; }
.hero-stage__chem {
  margin: 0 0 6px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500; line-height: 1.4;
  letter-spacing: 0.12em; text-transform: uppercase;
}
.hero-stage__chem a,
.hero-stage__chem span {
  display: inline-flex; align-items: center; gap: 9px;
  color: rgba(255, 255, 255, 0.84); text-decoration: none;
}
.hero-stage__chem a::before,
.hero-stage__chem span::before {
  content: ''; flex: none; width: 8px; height: 8px; border-radius: 2px;
  background: var(--color-brand-red);
  box-shadow: 0 0 10px rgba(240, 52, 54, 0.7);
}
.hero-stage__chem a:hover,
.hero-stage__chem a:focus-visible { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.hero-stage__chem a:focus-visible { outline: 2px solid #fff; outline-offset: 3px; border-radius: 2px; }
.hero-stage .hero-stage__name {
  margin: 0 0 2px;
  font-family: var(--font-display); font-weight: 700;
  font-size: 20px; line-height: 1.1; letter-spacing: -0.02em;
  color: #fff;
  overflow-wrap: anywhere;
  text-wrap: balance;
}
/* The way in: a label and a red arrow, no pill or box around them. */
.hero-stage__open {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 36px;
  color: #fff; font-size: 14px; font-weight: 600; line-height: 1;
  text-decoration: none;
}
.hero-stage__open:hover,
.hero-stage__open:focus-visible { color: #fff; text-decoration: none; }
.hero-stage__open:hover .hero-stage__open-label { text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }
.hero-stage__open:focus-visible { outline: 2px solid #fff; outline-offset: 4px; border-radius: 4px; }
.hero-stage__open-arrow {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--color-brand-red); color: #fff;
  box-shadow: 0 6px 18px -6px rgba(240, 52, 54, 0.8);
  transition: transform 260ms var(--ease-spring, var(--hs-ease));
}
.hero-stage__open-arrow svg { width: 14px; height: 14px; display: block; }
.hero-stage__open:hover .hero-stage__open-arrow { transform: translateX(4px); }

/* THE CONTROLS, loose under the stage: previous and next as round arrows at either end,
   and between them a track with the counter, the picker scale and the countdown line. */
.hero-stage__nav {
  grid-area: nav;
  display: flex; align-items: center; gap: 14px;
  width: min(100%, 320px);
}
.hero-stage__arrow {
  position: relative; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.26);
  background: transparent;
  color: #fff; cursor: pointer;
  transition: transform 200ms var(--hs-ease);
}
/* The red fill on hover fades in on its own layer (opacity only). */
.hero-stage__arrow::before {
  content: ''; position: absolute; inset: -1px; border-radius: 50%;
  background: var(--color-brand-red);
  opacity: 0;
  transition: opacity 200ms var(--hs-ease);
}
.hero-stage__arrow:hover::before { opacity: 1; }
.hero-stage__arrow:active { transform: scale(0.94); }
.hero-stage__arrow:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.hero-stage__arrow svg { position: relative; width: 18px; height: 18px; display: block; }

.hero-stage__track {
  flex: 1 1 auto; min-width: 0;
  display: grid;
  grid-template-areas: "count" "dots" "bar";
  justify-items: center;
}
.hero-stage__count {
  grid-area: count;
  font-family: var(--font-mono); font-size: 12px; line-height: 16px; letter-spacing: 0.04em;
  /* 0.86, not lower: over the red glow on a portrait tablet 0.72 measured 4.2:1 (AA needs 4.5). */
  color: rgba(255, 255, 255, 0.86);
  font-variant-numeric: tabular-nums;
}
.hero-stage__count b { color: #fff; font-weight: 600; }

/* The picker scale: one tick per product, the one on stage taller and red. */
.hero-stage__dots { grid-area: dots; display: none; }
.hero-stage__dot {
  position: relative; flex: 1 1 0; min-width: 0; height: 30px;
  padding: 0; margin: 0; border: 0; background: transparent; cursor: pointer;
}
.hero-stage__fill {
  position: absolute; left: 50%; bottom: 4px;
  width: 2px; height: 10px; margin-left: -1px; border-radius: 1px;
  background: rgba(255, 255, 255, 0.40);
  transform-origin: 50% 100%;
  transition: transform 450ms var(--hs-ease);
}
.hero-stage__fill::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  background: var(--color-brand-red);
  box-shadow: 0 0 8px rgba(240, 52, 54, 0.85);
  opacity: 0;
  transition: opacity 300ms var(--hs-ease);
}
.hero-stage__dot:hover .hero-stage__fill { transform: scaleY(1.5); }
.hero-stage__dot[aria-current="true"] .hero-stage__fill { transform: scaleY(2.2); }
.hero-stage__dot[aria-current="true"] .hero-stage__fill::after { opacity: 1; }
.hero-stage__dot:focus-visible { outline: 2px solid #fff; outline-offset: -2px; border-radius: 4px; }

/* The countdown line: fills red over the 10 s each product stays; gone once stopped. */
.hero-stage__bar {
  grid-area: bar;
  position: relative; display: block;
  width: min(100%, 140px); height: 2px; margin-top: 8px;
  border-radius: 2px; overflow: hidden;
  background: rgba(255, 255, 255, 0.14);
  transition: opacity 400ms var(--hs-ease);
}
.hero-stage__bar i {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--color-brand-red-deep), #ff5a4a);
  transform: scaleX(0); transform-origin: 0 50%;
}
.hero-stage.is-stopped .hero-stage__bar { opacity: 0; }

/* 1025 AND UP (round 4): the product, its caption centred under it, then the controls
   centred under the caption, as one unit in the right-hand column. The control row is
   380px at most, with the full picker scale between the arrows. (Round 3 put the caption
   in a column beside the product; round 4 tried a caption bar beside the controls too.
   Both made the caption a third column competing with the headline.) */
@media (min-width: 1025px) {
  .hero-stage__frame { row-gap: 10px; }
  .hero-stage__scene { width: min(100%, var(--hs-scene-wide)); }
  .hero-stage__nav { width: min(100%, 380px); gap: 16px; margin-top: 6px; }
  .hero-stage__track { justify-items: stretch; }
  .hero-stage__count { justify-self: center; }
  .hero-stage__dots { display: flex; }
  .hero-stage__bar { width: 100%; margin-top: 0; }
}
/* A short, wide laptop screen (section 2 has the same query): height is what limits the
   product there, so the small caption sits beside the product instead of under it and
   the controls run across the stage underneath. */
@media (min-width: 1025px) and (max-height: 719px) and (min-aspect-ratio: 17/10) {
  .hero-stage__frame {
    grid-template-columns: minmax(0, 1fr) clamp(170px, 30cqi, 230px);
    grid-template-areas: "scene info" "nav nav";
    column-gap: 24px;
    row-gap: 14px;
    justify-items: stretch;
    text-align: left;
  }
  .hero-stage__scene { justify-self: center; }
  .hero-stage__infos { align-self: center; padding-bottom: 6%; }
  .hero-stage__nav { width: 100%; gap: 18px; margin-top: 0; }
  .hero-stage__count { justify-self: start; }
}
/* Portrait tablets (the stage under the stacked copy, 560px wide and more, so 624 to 899px
   screens): the caption and the controls share a column beside the product, so the stage
   ends with the product. A full-width control line would end at the bottom of the first
   screen, exactly where the site's floating "Ask MightyBot" button sits. The picker scale
   is left out here (180px is too narrow for 23 marks); the arrows, swipe and the counter
   do the same. */
@media (max-width: 899px) {
  @container hstage (min-width: 560px) {
    .hero-stage__frame {
      grid-template-columns: minmax(0, 1fr) clamp(170px, 30cqi, 230px);
      grid-template-rows: 1fr auto auto 1fr;
      grid-template-areas: "scene ." "scene info" "scene nav" "scene .";
      column-gap: 24px;
      row-gap: 0;
      justify-items: stretch;
      text-align: left;
    }
    .hero-stage__scene { width: min(100%, var(--hs-scene-wide)); justify-self: center; }
    .hero-stage__infos { align-self: end; }
    .hero-stage__nav { width: 100%; gap: 10px; margin-top: 20px; }
    .hero-stage__track { justify-items: center; }
    .hero-stage__count { justify-self: center; }
    .hero-stage__bar { width: 100%; margin-top: 8px; }
  }
}

/* ---------- 6. IDLE MOTION, REDUCED MOTION, OFF SCREEN ---------- */

/* The product sways a few degrees on its turntable, floats, and its shadow breathes with
   it. Runs on the displayed figures only (display:none stops the rest). */
.hero-stage__sway  { animation: hsSway 11s ease-in-out infinite alternate; }
.hero-stage__float { animation: hsFloat 5.5s ease-in-out infinite alternate; }
.hero-stage__shadow { animation: hsShadow 5.5s ease-in-out infinite alternate; }
.hero-stage__sheen i { animation: hsGlint 11s ease-in-out infinite alternate; }
@keyframes hsSway   { from { transform: perspective(1000px) rotateY(-12deg); } to { transform: perspective(1000px) rotateY(12deg); } }
@keyframes hsFloat  { from { transform: translateY(0); } to { transform: translateY(-9px); } }
@keyframes hsShadow { from { transform: scaleX(1); opacity: 1; } to { transform: scaleX(0.86); opacity: 0.7; } }
@keyframes hsGlint  { from { transform: translateX(-30%); } to { transform: translateX(22%); } }

@media (prefers-reduced-motion: reduce) {
  body .hero.hero--stage.hero-mesh .hero__redglow,
  .hero--stage .hero-stage__sway,
  .hero--stage .hero-stage__float,
  .hero--stage .hero-stage__shadow,
  .hero--stage .hero-stage__sheen i,
  .hero--stage .hero-stage__ring { animation: none; }
  .hero--stage .hero-stage__deck,
  .hero--stage .hero-stage__arrow,
  .hero--stage .hero-stage__arrow::before,
  .hero--stage .hero-stage__open-arrow,
  .hero--stage .hero-stage__fill,
  .hero--stage .hero-stage__fill::after { transition: none; }
}

/* Off screen (IntersectionObserver in view.js): every animation in the hero's upper
   part waits. The industries ticker is left running: it is still in view when the
   headline and the stage have scrolled away. */
.hero--stage.is-offscreen::before,
body .hero.hero--stage.hero-mesh.is-offscreen .hero__redglow,
.hero--stage.is-offscreen .hero-glass *,
.hero--stage.is-offscreen .hero-glass__sweep::before,
.hero--stage.is-offscreen .hero__copy *,
.hero--stage.is-offscreen .hero-stage *,
.hero--stage.is-offscreen .hero-stage__ring { animation-play-state: paused; }
