/* ============================================================
   PRODUCT HERO + MANAGED DASHBOARD
   ------------------------------------------------------------
   The two-column product hero shared by every hosting product:
   copy (label, headline, lead, CTAs, price, checklist) on the
   left, the product's dashboard artwork on the right, and a
   four-up trust strip closing the band.

   THE LAYOUT NEVER CHANGES BETWEEN PRODUCTS. Everything that
   differs — label tint, checks, price, CTA fill, background
   glow, the artwork file — reads from the `--product-*` tokens
   that `data-product` swaps in (see tokens.css). Building the
   next product page is: copy this markup, change
   `data-product`, point the <img> at that product's dashboard
   artwork. Nothing in this file is product specific.

   THE ARTWORK IS A TRANSPARENT WEBP CUT-OUT (the managed
   dashboard window, its status cards and its floating chips,
   composed as one file with the float chips already breaking
   the window's edges). It therefore gets NO card, NO plate and
   NO padding — a frame would draw a second outline around a
   composition that already declares its own edges — and it
   sits directly on the band so the background glow reads as
   the room the dashboard is in, which is the whole point of
   the background treatment. See the `data-bare` history in
   components.css for the opposite case (artwork whose strokes
   were clipped and needed a declared edge).
   ============================================================ */

/* ---------- The band ----------
   `.page-hero` supplies padding, isolation and the hairline foot;
   this overrides its flat grey with the product's atmosphere. The
   hairline itself is token-gated (`--product-hero-foot`) because a
   band that fades to white at its foot must not draw a rule through
   the fade.

   Four radial glows over a faceted wallpaper, then a foot fade to
   page white. The strongest glow is anchored behind the dashboard
   column (right, upper third) so the artwork appears lit by the band
   instead of pasted onto it; the two supporting glows keep the far
   right and the bottom-left from going flat. Nothing crosses the copy
   column at more than a whisper: the left third — label, headline,
   lead, price, checks — sits on the near-white end of the wallpaper,
   so every glyph stays on the band's lightest surface and the worst
   case for contrast is the wash colour alone.

   Two layers carry the drama the dashboard needs to emerge from:
   the 0.18-alpha circle at 75% 40% — the requested pool of product
   light right behind the dashboard slot — and a larger, softer
   0.13-alpha field above and behind it, so the glow has depth
   instead of being one flat blob. Strength still stays under a
   quarter alpha and every cone keeps right of the copy column: the
   label, headline, lead, price and checks all sit on the near-white
   end of the wallpaper, so worst-case text contrast is still the
   wash colour alone.

   The wallpaper and the foot fade are both per-product tokens, so a
   product without them (the two transfer pages) paints the original
   white-to-wash gradient and keeps its hairline. */
.product-hero {
  /* The flat colour sits UNDER the wallpaper SVG so the element's own
     colour shows through any antialiased seam between facets, and it
     doubles as the no-SVG fallback. */
  background-color: var(--product-wallpaper-base, transparent);
  background-image:
    /* The foot fade. Painted ABOVE everything else so the wallpaper, the
       four glows and the band colour all dissolve together into the page
       white, which is what the gap around the next `section--card` band
       already is. `none` by default: a product hero with no wallpaper
       (the two transfer pages) paints nothing extra and is unchanged.

       Both stops are white, so the ramp only ever raises alpha — there
       is no grey midpoint to band. The 72% stop sits below the dashboard
       artwork so the product shot stays crisp; the fade owns the trust
       strip and the band's foot, which is the last fifth of the hero. */
    var(--product-wallpaper-fade, none),
    radial-gradient(
      circle at 75% 40%,
      rgba(var(--product-accent-rgb), 0.18) 0%,
      rgba(var(--product-accent-rgb), 0) 60%
    ),
    radial-gradient(
      72% 96% at 88% 16%,
      rgba(var(--product-accent-rgb), 0.13) 0%,
      rgba(var(--product-accent-rgb), 0) 74%
    ),
    radial-gradient(
      38% 52% at 99% 80%,
      rgba(var(--product-accent-rgb), 0.1) 0%,
      rgba(var(--product-accent-rgb), 0) 72%
    ),
    radial-gradient(
      30% 44% at 4% 97%,
      rgba(var(--product-accent-rgb), 0.06) 0%,
      rgba(var(--product-accent-rgb), 0) 70%
    ),
    /* The surface the four glows are lit on top of. A product with a
       `--product-wallpaper` token gets its faceted-mesh SVG (`cover`,
       top-aligned so the bloom behind the dashboard stays in frame and
       the whitened foot is cropped rather than shown); a product
       without one — the two transfer pages — falls through to the
       original white-to-wash gradient and is visually unchanged. */
    var(--product-wallpaper, linear-gradient(180deg, var(--white) 0%, var(--product-accent-wash) 100%));
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center top;
  /* `.page-hero` supplies "the hairline foot". A wallpaper product turns
     it off: the band now fades to page white, and a 1px slate rule
     floating in the middle of that fade reads as a leftover seam rather
     than as a boundary — the trust strip's own hairline already closes
     the band properly. A product with no wallpaper keeps it. */
  border-bottom: var(--product-hero-foot, 1px solid var(--slate-200));
}

/* The default hero texture across the site is the dot grid, and this
   band deliberately does not paint it: the band's texture is the
   product's faceted wallpaper instead (see `--product-wallpaper` in
   tokens.css, applied to `.product-hero` itself). Two patterns on one
   band would read as texture fighting texture. Painting `none` here
   (rather than removing the rule in components.css) keeps the other
   thirty heroes' graph paper exactly as it is. */
.product-hero::after {
  background-image: none;
}

/* ---------- Hero-only container ----------
   The band alone gets the wider room — 1440px instead of the
   site's 1360px container — with slightly deeper side padding on
   wide screens. Nothing else on the site changes width; the extra
   space exists so the dashboard composition can read as the hero
   of the section instead of an illustration in the margin, and so
   the headline stops wrapping every two words. */
.product-hero .container--wide {
  max-width: 1440px;
  padding-inline: clamp(1.25rem, 3.4vw, 3rem);
  /* Positioned above the decorative dot clusters (`.product-hero__dots`),
     which paint at z-index 0 directly on the band. */
  position: relative;
  z-index: 1;
}

/* ---------- The two columns ---------- */
.product-hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.18fr);
  gap: clamp(2rem, 3vw, 2.75rem);
  align-items: center;
}

.product-hero__copy {
  min-width: 0;
}

/* The lead is measured tighter than base.css's 62ch: in a column this
   narrow a full-width paragraph becomes a wall before the CTA row. */
.product-hero .lead {
  max-width: 52ch;
}

/* The 22ch cap on `.page-hero h1` suits single-column heroes; in a
   two-column band it turns the headline into a five-line tower and
   makes the whole page feel narrow. Here the copy column is the
   measure, `balance` keeps the rag even, and the size steps back one
   notch so the headline reads in two or three lines like the mock. */
.product-hero h1 {
  max-width: none;
  font-size: clamp(2.5rem, 3vw + 1.35rem, 3.75rem);
  text-wrap: balance;
}

/* A headline may split across two tones — promise in ink, payoff in the
   product accent ("More resources. Same simplicity."). At display sizes
   the accent only needs 3:1, which every product's strong stop clears;
   body-sized text must never wear this span. */
.product-hero__h1-accent {
  color: var(--product-accent-strong);
}

/* ---------- The label, retinted to the product ----------
   `.page-hero__label` / `.page-hero__icon` carry the house tile + word
   lock-up; only the colours are the product's. Every hosting product
   now has its own accent here — WordPress included, which used to
   inherit the emerald defaults and render pixel-identical to the
   unscoped classes. */
.product-hero .page-hero__label {
  color: var(--product-accent-strong);
}

.product-hero .page-hero__icon {
  background: var(--product-accent-wash);
  border-color: var(--product-accent-soft);
  color: var(--product-accent-strong);
}

/* cPanel's badge carries the vendor's real cP monogram — a transparent
   WebP — instead of a traced glyph, so its tile inverts the pattern
   above: the fill IS the product accent and the mark wears white. A
   raster mark cannot take `currentColor`, which is exactly why the tile
   goes solid orange. The 50px geometry, radius and hairline stay, so
   the label still reads as the same lock-up as every other hero. The
   artwork's canvas is square with the glyph centred, so 46px of img
   lands the glyph at roughly 45×30px inside the tile. */
[data-product="cpanel"] .page-hero__icon {
  background: var(--product-accent);
  border-color: var(--product-accent-strong);
}

[data-product="cpanel"] .page-hero__icon img {
  display: block;
  width: 46px;
  height: auto;
}

/* ---------- The CTAs ----------
   Same button component, product fill. `.btn--brand` keeps its hover
   lift and brand shadow from components.css; only the ramp changes,
   and only inside this hero — a product page's other buttons keep the
   house emerald, because the product accent is for the hero's voice,
   not a re-skin of the brand. */
.product-hero .btn--brand {
  background: var(--product-grad-btn);
}

.product-hero .btn--brand:hover {
  background: var(--product-grad-btn-hover);
}

/* The arrow rides at cap height inside the label-sized button text. */
.product-hero__arrow {
  width: 1.1em;
  height: 1.1em;
  flex-shrink: 0;
}

/* ---------- The pricing callout ----------
   "Plans from" is a caption, the number is the message. The amount is
   the only large type in the copy column that wears colour, and by
   default it wears `strong` (the text-safe stop of the ramp) rather
   than the accent itself, so it clears 4.5:1 on the wash. A product
   may re-colour it with `--product-price-color` — cPanel's mock keeps
   the price house green while its accents are orange. */
.product-hero__price {
  margin: var(--sp-6) 0 0;
}

.product-hero__price-label {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 650;
  letter-spacing: var(--tracking-label);
  color: var(--slate-500);
}

.product-hero__price-amount {
  font-family: var(--font-display);
  font-size: var(--fs-3xl);
  font-weight: 800;
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--product-price-color, var(--product-accent-strong));
}

.product-hero__price-unit {
  font-size: var(--fs-md);
  font-weight: 650;
  /* The unit now carries "first term" on the product heroes, which is
     a fact the visitor is being asked to act on — slate-600 keeps it
     readable on the pale product bases it sits on. */
  color: var(--slate-600);
}

/* The house honesty clause, kept but demoted to one short line: the
   full renewals-included sentence is still on the page, printed with
   the tiers themselves where the prices it qualifies actually live. */
.product-hero__price-note {
  display: block;
  margin-top: var(--sp-2);
  /* The renewals sentence sat at 13px slate-500 while the amount above
     it ran at 30px — a 2.3x drop across a two-line price. Body size and
     slate-600 closes the gap without competing with the number. */
  font-size: var(--fs-base);
  color: var(--slate-600);
}

/* The checklist sits under the price; the transfer hero's primary
   action is a form instead, so it gets the same top spacing the
   button row would have. */
.product-hero__form {
  margin: var(--sp-8) 0 0;
  max-width: 620px;
}

/* ---------- The feature checklist ----------
   SUPERSEDED 2026-10-08 — the rules moved to components.css, under "The
   feature checklist". Ticks already mean "included" everywhere on this
   site, so they wear the accent in filled circles (the mock's treatment)
   and stay a quiet one-row list rather than a second feature grid.

   It is a HERO part, not a product-hero part: the Website Builder and the
   legacy-hero product pages carry this list on sheets that never load
   this file, which is exactly why it no longer lives here. The
   three-item cap travels with the rules. */

/* Nothing in this file styles the checklist or the trust strip any more;
   both are shared hero parts now. */

/* ---------- The dashboard artwork ----------
   THE COMPOSITION IS BIGGER THAN ITS SLOT. The window fills most of
   the artwork's canvas, so rendering the file at plain column width
   is what made the dashboard read as a decorative icon. It is drawn
   at `--product-dash-scale` of the column (1.4 by default — cPanel
   rides 1.5), pulled `--product-dash-shift` left so the window's
   sidebar rides the gutter, and allowed to run off the band's right
   edge the way the product shot does in the reference mock —
   `.page-hero`'s `overflow: hidden` guarantees that never costs a
   scrollbar. The oversize steps down at narrower widths so no
   important part of the window is cropped on the way to the stacked
   layout.

   Still bare: no plate, no border, no radius (see the module note),
   but grounded with a soft drop-shadow and lit from behind by an
   accent halo, so the panel sits IN the band's light instead of
   hovering over a flat wash.

   The two HTML status chips float in the artwork's quiet zones and
   tuck behind the window's top and bottom edges for layered depth;
   their copy is product specific and lives in each page's markup.
   They are OPTIONAL: cPanel ships none, because its artwork already
   carries three status cards of its own.

   The bob is deliberately tiny (10px over 8s) with a sub-half-degree
   tilt: enough that the panel feels suspended, not enough to notice as
   motion; the chips drift a touch slower on top of it. Those numbers
   are the split hero's own `heroFloatSlow` (split-hero.css), so the
   hosting heroes and the two email heroes now float on one cadence
   instead of three. The global prefers-reduced-motion block in
   base.css stills all of it. */
.product-hero__dash {
  position: relative;
  justify-self: end;
  align-self: center;
  width: 100%;
  min-width: 0;
  animation: product-dash-bob 8s ease-in-out infinite alternate;
}

/* The halo: a soft pool of product light directly behind the window.
   Gradient softness does the blurring — no filter, no repaint cost. */
.product-hero__dash::before {
  content: "";
  position: absolute;
  inset: -12% -4% -10% -6%;
  z-index: 0;
  background: radial-gradient(
    54% 54% at 56% 42%,
    rgba(var(--product-accent-rgb), 0.22) 0%,
    rgba(var(--product-accent-rgb), 0) 72%
  );
}

.product-hero__dash img {
  position: relative;
  z-index: 1;
  display: block;
  width: calc(var(--product-dash-scale, 1.4) * 100%);
  height: auto;
  margin-left: var(--product-dash-shift, -10%);
  filter:
    drop-shadow(0 32px 64px rgba(15, 23, 42, 0.16))
    drop-shadow(0 10px 24px rgba(15, 23, 42, 0.08));
}

@keyframes product-dash-bob {
  from {
    transform: translateY(0) rotate(-0.25deg);
  }
  to {
    transform: translateY(-10px) rotate(0.35deg);
  }
}

/* ---------- Behind the artwork: an ambience layer the shot can occlude
   ------------------------------------------------------------
   Website Backup is the first product hero to carry a flying-glyph
   field (`.backup-stream`, split-hero.css) rather than only a
   wallpaper. On the split heroes that field sits at z-index 0 on the
   band, with the artwork at z-index 1 — so a glyph could pass BESIDE
   the shot but never FROM BEHIND IT.

   Here the stream is a child of `.product-hero__inner` (the grid), so
   the grid becomes the positioning context and the two columns can be
   ranked ABOVE it. The stacking order inside the grid is then:

     .backup-stream            0    the band: glyphs rise out of it
     .product-hero__copy       1    the words are never occluded
     .product-hero__dash       —    holds the artwork

   and inside `.product-hero__dash` the artwork img is already at
   z-index 1 over that box's own z-index-0 halo, so a glyph travelling
   near the shot's near edge disappears UNDER it: the snapshots read as
   circling the window rather than as stickers on the band.

   Two constraints this keeps:

   - `z-index` here is per-grid, not global, so the field still paints
     under the band's foot fade and over its wallpaper — the artwork
     simply gets a second plane to occlude.
   - The dash carries an `animation` (the bob), which makes it a
     stacking CONTEXT: the stream must therefore be a SIBLING of the
     dash column inside the grid, never a child of it, or the bob would
     drag the glyphs up and down with the window. */
.product-hero__inner {
  position: relative;
}

.product-hero__inner > .backup-stream {
  z-index: 0;
}

.product-hero__inner > .product-hero__copy,
.product-hero__inner > .product-hero__dash {
  position: relative;
  z-index: 1;
}

/* ---------- Floating status chips ----------
   Two small glass cards in the artwork's two quiet zones — above the
   window's top edge (nothing in the file there but air) and below its
   bottom edge (the window's own ground shadow) — so they can tuck
   behind the window and read as layers of one composition instead of
   stickers on top of it. White glass over the wash, accent check,
   soft double shadow, a degree or two of tilt, and a slow counter-
   drift on top of the panel's bob.

   They restate the checklist in the copy column and are therefore
   decorative: `aria-hidden`. Their titles are product specific
   ("WordPress Updated") and live in each product page's markup. */
.product-hero__chip {
  position: absolute;
  z-index: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 12px 16px;
  border: 1px solid rgba(226, 232, 240, 0.9);
  border-radius: var(--r-lg);
  background: rgba(255, 255, 255, 0.9);
  box-shadow:
    0 18px 40px rgba(15, 23, 42, 0.12),
    0 4px 12px rgba(15, 23, 42, 0.06);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transform: rotate(var(--chip-rot, 0deg));
}

.product-hero__chip-icon {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: var(--r-full);
  background: var(--product-accent-soft);
  color: var(--product-accent-strong);
}

.product-hero__chip-icon svg {
  width: 13px;
  height: 13px;
}

.product-hero__chip strong {
  display: block;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  color: var(--ink-900);
  white-space: nowrap;
}

.product-hero__chip div span {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--slate-500);
  white-space: nowrap;
}

.product-hero__chip--a {
  --chip-rot: -1.8deg;
  top: 3%;
  left: 29%;
  animation: product-chip-drift 7s ease-in-out infinite alternate;
}

.product-hero__chip--b {
  --chip-rot: 1.5deg;
  bottom: 3%;
  left: 12%;
  animation: product-chip-drift 9s 0.6s ease-in-out infinite alternate;
}

@keyframes product-chip-drift {
  from {
    transform: translateY(0) rotate(var(--chip-rot, 0deg));
  }
  to {
    transform: translateY(-7px) rotate(var(--chip-rot, 0deg));
  }
}

/* ---------- The trust strip ----------
   MOVED to components.css on 2026-10-08, under "The trust strip" — see
   there for the rules, the reason it left this file, and the explicit
   z-index that lets it sit above the split heroes' envelope layer.
   It is a HERO part, not a product-hero part, and every product page on
   the site now carries one. */

/* ---------- The dot matrix texture ----------
   The Website Builder's signature detail, promoted into the product
   system as a small static cluster: a field of ~2px dots on a 16px
   pitch, tinted from `--product-accent-rgb` so every product's dots are
   automatically its own (WordPress blue, cPanel orange, Hosting Plus
   amber, VPS blue) with no per-product CSS. Two fixed clusters per
   hero, both anchored to the band's quiet zones — one in the air above
   the dashboard's top edge, one in the dead space under the checklist —
   and each dissolved through a radial mask so the field has no hard
   rectangular edge. Decoration only: `aria-hidden` in the markup and
   `pointer-events: none` here, z-index 0 so the copy container (z-index
   1) always paints above them, and hidden on the stacked layout, where
   the dissolve puts headline and artwork edge to edge and the band has
   no quiet zone left for a cluster to sit in. */
.product-hero__dots {
  position: absolute;
  z-index: 0;
  width: 210px;
  height: 150px;
  pointer-events: none;
  background-image: radial-gradient(
    rgba(var(--product-accent-rgb), 0.38) 1.6px,
    transparent 1.7px
  );
  background-size: 16px 16px;
  mask-image: radial-gradient(closest-side, #000 30%, transparent 78%);
  -webkit-mask-image: radial-gradient(closest-side, #000 30%, transparent 78%);
}

.product-hero__dots--a {
  top: 5%;
  right: 2%;
}

.product-hero__dots--b {
  bottom: 22%;
  left: 0.5%;
  width: 170px;
  height: 120px;
}

/* ---------- The hero visual column (transparent cut-outs) ----------
   A SIBLING of `.product-hero__dash` for artwork that carries its
   OWN alpha — the two transfer heroes use it for the moving van
   (`domains/transfer`) and the moving truck (`domains/bulk-transfer`).

   It draws NO frame. The dashboards under `.product-hero__dash` set
   the precedent: a cut-out that declares its own edges must not be
   given a second outline, or it reads as a picture in a box instead
   of an object standing in the band. So there is no border, radius or
   white plate here — only a pool of product light behind the artwork
   (`::before`) and an alpha-shaped drop-shadow under it (the `--bare`
   rules below), so the cut-out sits inside the band's colour.

   The column lifts on the same slow bob the dashboard uses, so the
   two hero styles feel like one system. */
.product-hero__visual {
  position: relative;
  justify-self: end;
  align-self: center;
  width: 100%;
  min-width: 0;
  animation: product-dash-bob 8s ease-in-out infinite alternate;
}

/* The halo: a default pool of product light behind the artwork,
   refined by the `--bare` variant below. */
.product-hero__visual::before {
  content: "";
  position: absolute;
  inset: -14% -6% -12% -8%;
  z-index: 0;
  background: radial-gradient(
    58% 58% at 50% 44%,
    rgba(var(--product-accent-rgb), 0.24) 0%,
    rgba(var(--product-accent-rgb), 0) 74%
  );
}

/* The base image sizing, shared by every visual. No forced aspect:
   a wide photo or a cut-out keeps its own ratio and the column
   simply takes the image's height. */
.product-hero__visual img {
  display: block;
  width: 100%;
  height: auto;
}

/* ---------- The bare (transparent cut-out) variant ----------
   The treatment both transfer heroes use. The artwork (the moving
   van, the moving truck) is a transparent cut-out with its own alpha,
   so it is dropped straight onto the band with NO frame — a cut-out
   that declares its own edges must not be given a second outline, or
   it reads as a picture in a box instead of an object standing in the
   band (the same rule `.product-hero__dash` follows).

   The cyan `::before` halo is widened so the artwork is lit from
   behind by the hero's own glow, and it is grounded with a soft
   drop-shadow filter following its alpha silhouette instead of a
   rectangular box-shadow — the shadow traces the shape, not a
   bounding box. */

/* A wider, softer pool of product light behind the cut-out so the
   transparent artwork sits inside a body of colour. */
.product-hero__visual--bare::before {
  inset: -20% -10% -16% -12%;
  background: radial-gradient(
    62% 62% at 50% 50%,
    rgba(var(--product-accent-rgb), 0.28) 0%,
    rgba(var(--product-accent-rgb), 0) 72%
  );
}

/* The alpha-shaped grounding shadow. `drop-shadow` follows the
   image's transparent edges, so the van casts a van-shaped shadow
   rather than the rectangle a box-shadow would draw. */
.product-hero__visual--bare img {
  width: 100%;
  height: auto;
  filter: drop-shadow(0 26px 32px rgba(15, 23, 42, 0.22));
}

/* ================= ENTRANCE =================
   The same choreographed arrival the domains hero runs: the copy
   column steps in block by block (blur + 22px rise, 900ms, staggered
   by `--enter-delay` in the markup), the dashboard artwork makes the
   bigger, slower rise the planet makes on the domains pages, the dot
   clusters fade in behind it, the floating chips drop in and settle
   into their drift, and the trust strip closes the sequence. CSS-only,
   `both` fill so nothing flashes, keyed off html.js (content is never
   hidden if scripts fail) and only under no-preference for motion.

   The artwork's idle bob and the chips' drift are listed AFTER the
   entrance in the same `animation` declaration and delayed until the
   entrance lands, so the loop inherits the stage with no snap: the
   entrance's final frame is the loop's first (transform: none reads
   as translateY(0)). */
@media (prefers-reduced-motion: no-preference) {
  html.js .product-hero [data-hero-enter] {
    animation: productEnter 900ms var(--ease-out) var(--enter-delay, 0ms) both;
  }

  html.js .product-hero .product-hero__dash[data-hero-enter],
  html.js .product-hero .product-hero__visual[data-hero-enter] {
    animation: productRise 1300ms var(--ease-out) var(--enter-delay, 0ms) both;
  }

  html.js .product-hero__dots {
    animation: productFadeIn 1600ms var(--ease-out) 500ms both;
  }

  html.js .product-hero__chip--a {
    animation:
      productChipIn 650ms var(--ease-out) 760ms both,
      product-chip-drift 7s ease-in-out 2.01s infinite alternate;
  }

  html.js .product-hero__chip--b {
    animation:
      productChipIn 650ms var(--ease-out) 880ms both,
      product-chip-drift 9s ease-in-out 2.13s infinite alternate;
  }
}

/* On wide screens the artwork's idle bob resumes once the rise has
   landed; on the stacked layout the bob is off by design, so the rise
   alone plays. */
@media (prefers-reduced-motion: no-preference) and (min-width: 981px) {
  html.js .product-hero .product-hero__dash[data-hero-enter],
  html.js .product-hero .product-hero__visual[data-hero-enter] {
    animation:
      productRise 1300ms var(--ease-out) var(--enter-delay, 0ms) both,
      product-dash-bob 8s ease-in-out calc(var(--enter-delay, 0ms) + 1300ms) infinite alternate;
  }
}

@keyframes productEnter {
  from { opacity: 0; transform: translate3d(0, 22px, 0); filter: blur(6px); }
  to { opacity: 1; transform: none; filter: none; }
}

@keyframes productRise {
  from { opacity: 0; transform: translate3d(0, 60px, 0) scale(0.96); }
  to { opacity: 1; transform: none; }
}

@keyframes productChipIn {
  from { opacity: 0; transform: translate3d(0, 16px, 0) rotate(var(--chip-rot, 0deg)); }
  to { opacity: 1; transform: translate3d(0, 0, 0) rotate(var(--chip-rot, 0deg)); }
}

@keyframes productFadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* ---------- Responsive ----------
   The hero stacks at 980px — the point where a 52ch headline column
   and the artwork can no longer both hold their minimums. On the
   stack the copy column dissolves (see the display:contents note in
   the media query) so the fold order on a phone is label → headline →
   artwork → lead → CTA: the product shot arrives before the fold runs
   out instead of after the whole copy column. The artwork renders at
   a capped width with only a whisper of bleed, because at 375px a 3:2
   panel scaled to the gutter is already ~250px tall and sharp. The
   floating status chips are wide-screen polish and hide entirely
   below 980px.

   The strip goes 4 → 2 → 1. Dividers are a wide-screen affordance:
   at two columns the vertical rule would sit mid-row-gap and read as
   a stray border, so the rows just breathe instead. */
@media (max-width: 1200px) {
  .product-hero__dash img {
    width: 118%;
    margin-left: -4%;
  }
}

@media (max-width: 980px) {
  .product-hero__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  /* The copy column dissolves on the stack so its pieces can
     interleave with the artwork: label and headline first, then the
     dashboard, then the lead, CTAs, price and checks. A phone reader
     meets the product shot before the fold runs out, rather than
     scrolling past the whole copy column to reach it. DOM order is
     unchanged, so screen readers and tab order still read the copy
     straight through.

     `display: contents` turns the children into grid items of
     `__inner`, where the grid gap would land between every one of
     them — so the gap goes to 0 and each block keeps its own margin,
     which is the same spacing the column already had. */
  .product-hero__copy {
    display: contents;
  }

  .product-hero__copy > * {
    min-width: 0;
  }

  .product-hero__dash {
    order: 1;
    margin: var(--sp-8) 0 var(--sp-6);
    justify-self: center;
    align-self: center;
    max-width: 680px;
    animation: none;
  }

  /* The hero visual stacks on the same slot as the dashboard —
     it interleaves between the headline and the lead, caps at the
     same width, and stops its bob (a lifting image on a stacked
     phone layout reads as an accident, not as polish). */
  .product-hero__visual {
    order: 1;
    margin: var(--sp-8) 0 var(--sp-6);
    justify-self: center;
    align-self: center;
    max-width: 620px;
    animation: none;
  }

  .product-hero__visual img {
    width: 100%;
  }

  .product-hero__copy > .lead,
  .product-hero__copy > .btn-row,
  .product-hero__copy > .product-hero__price,
  .product-hero__copy > .product-hero__form,
  .product-hero__copy > .product-hero__checks {
    order: 2;
  }

  .product-hero__dash img {
    width: 112%;
    margin-left: -6%;
  }

  .product-hero__chip {
    display: none;
  }

  .product-hero__dots {
    display: none;
  }
}

/* The strip's own 980px and 560px steps moved to components.css with the
   rules — they are the shared component's breakpoints now, not this
   hero's. */