/* ==========================================================
   pages/humidor.css — The Walk-In Humidor
   The showcase leg of the site. Sparse by design: five folds,
   big imagery, motion doing most of the talking.

   Keeps the cigar side's warm amber-on-brown grade (unlike
   gator-tobacco.css, which runs its own night palette), and
   shares the plate contract with js/immersive.js —
   [data-plate], [data-plate-trigger], [data-scroll-progress],
   [data-count-to], [data-glow].

   Written mobile-first: base rules target narrow portrait,
   min-width queries add desktop.
   ========================================================== */

.page-humidor {
  --plate-hero:      url('../../assets/images/plates/shop_interior_hero_wide_03.webp'); /* 3:2 — real photo */
  --plate-inventory: url('../../assets/images/plates/shop_interior_inventory_wide_01.webp'); /* 3:2 — real photo */
  --plate-ritual:    url('../../assets/images/plates/humidor_ritual_wide.svg');    /* 16:9 */
  /* --plate-shelf does double duty: the gallery's photographic bed
     and its own slide in the carousel. Same frame, two roles — the
     bed runs at roughly a quarter strength, so they do not read as
     a repeat. Change one and you change both. */
  --plate-shelf:     url('../../assets/images/plates/shop_interior_shelf_wide_01.webp'); /* 3:2 — real photo, gallery bed + slide */
  --plate-shelf-2:   url('../../assets/images/plates/shop_interior_shelf_wide_02.webp'); /* 3:2 — real photo, slide */
  --plate-cedar:     url('../../assets/images/plates/humidor_cedar_detail.svg');   /* 4:5  */
  --plate-climate:   url('../../assets/images/plates/humidor_climate_detail.svg'); /* 4:3  */
  --grain:           url('../../assets/images/plates/grain_tile.svg');

  --ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
  --rule: rgba(197, 160, 89, 0.16);
  --gutter: clamp(1.1rem, 4vw, 3rem);

  position: relative;
  min-height: 100svh;
  background-color: var(--color-surface);
  color: var(--color-on-surface);
  font-family: var(--font-body);
  /* overflow:clip (not hidden) — see css/pages/brand.css .brand-page for why */
  overflow: clip;
}

/* ── Fixed background plates ──────────────────────────────
   LOCKED. These never translate — on desktop or mobile. Only
   opacity animates, so each plate rasterises once and the
   compositor crossfades them with no repaint. Height is pinned
   to --locked-bg-height (set from window.screen.height by
   main.js) so the mobile address bar sliding in and out cannot
   resize the plate mid-scroll, which is the actual cause of the
   fixed-background jank this site works around elsewhere. */
.hm-plate {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--locked-bg-height, 100lvh);
  z-index: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 0;
  pointer-events: none;
  transform: translateZ(0);
  transition: opacity 1.3s var(--ease-out-soft);
  will-change: opacity;
}
.hm-plate.is-active { opacity: 1; }

.hm-plate[data-plate="hero"]      { background-image: var(--plate-hero); }
.hm-plate[data-plate="inventory"] { background-image: var(--plate-inventory); }
.hm-plate[data-plate="ritual"]    { background-image: var(--plate-ritual); }

/* ── Plate atmosphere — scrim + vignette ──────────────────
   Real photography carries far more local contrast than the
   placeholder art did, and white display type sits directly on
   these plates. Two jobs in one paint, cheapest to composite in
   this order:

     1. a flat scrim that lifts overall density
     2. a radial vignette that falls off toward the corners,
        pulling the eye to the centre where the copy lives

   Both ride on the plate itself rather than on each section, so
   they travel with the image everywhere it appears and crossfade
   with it — there are no per-section overlays to keep in sync.
   Near-black is warm (8,6,5) not neutral; a pure-black vignette
   reads grey and cheap against this page's amber grade.

   Neither scrolls. This rides on .hm-plate, which is position:
   fixed at a height locked to the device screen, so the vignette
   stays welded to the plate while the copy travels over it. That
   is the whole effect: the frame holds still and darkens, the
   words move through the pocket of light in the middle.

   SIZING IS farthest-corner, AND THAT MATTERS. It is the CSS
   default, and it means the outer stop lands exactly on whichever
   corner is furthest from the gradient's centre — automatically,
   at every viewport size and aspect ratio, with no media queries
   and no magic numbers. Give the ellipse an explicit size instead
   and you pin it to one shape: at the 115% 90% this rule used to
   carry, the ellipse was larger than the plate, the corners sat
   around 0.66 of its radius, and the ramp topped out long before
   reaching them — which is why they stayed stubbornly grey no
   matter how far --plate-vignette was pushed. Override
   --plate-vignette-size only if a plate genuinely wants a shape
   that ignores its own corners.

   Tune per plate with --plate-scrim / --plate-vignette /
   --plate-vignette-inner / --plate-vignette-size below. All
   inherit into this pseudo-element, so setting them on the plate
   element is enough. */
.hm-plate::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(
      ellipse var(--plate-vignette-size, farthest-corner) at 50% 45%,
      transparent var(--plate-vignette-inner, 30%),
      rgba(8, 6, 5, calc(var(--plate-vignette, 0.55) * 0.20)) 42%,
      rgba(8, 6, 5, calc(var(--plate-vignette, 0.55) * 0.55)) 68%,
      rgba(8, 6, 5, var(--plate-vignette, 0.55)) 100%
    ),
    linear-gradient(
      rgba(10, 7, 6, var(--plate-scrim, 0.26)),
      rgba(10, 7, 6, var(--plate-scrim, 0.26))
    );
}

/* The hero interior is the inverse problem to the inventory wall.
   Its brightest region is the lit doorway at dead centre, which is
   exactly where the largest display type on the page sits — so the
   vignette is structurally the wrong tool here: it clears the
   centre by design and contributes almost nothing where the damage
   is. The flat scrim does the real work on this plate; the
   vignette only shapes the edges. Read the two below in that order.

   Scrim is 0.48 rather than the 0.55 the first hero candidate
   needed. That frame put 26% of the headline band past 0.75; this
   one is shot back far enough to halve it to 15%, so the same
   final tone is reachable with less flat fill — which is the whole
   point, since flat fill is what eats the photograph's own
   contrast. If this plate is ever reshot, that 15% is the number
   to beat, not the tuning below. */
.hm-plate[data-plate="hero"] {
  --plate-scrim: 0.32;
  --plate-vignette: 0.52;
  --plate-vignette-inner: 20%;
}

/* The inventory wall is by far the busiest plate in the set —
   ~8% of the band behind the pull-quote is bright label stock and
   price tags, against ~0% for the placeholder it replaced — so it
   runs heavier than the default on every axis.

   With the edges now doing the work, the flat scrim comes DOWN
   rather than up (0.48 → 0.40). Scrim darkens the centre as much
   as the corners, so leaning on it would have closed the middle
   back up; the point here is a centre that stays open for the
   copy while the frame falls away around it.

   Sizing is left at farthest-corner — see the shared rule above
   for why that is what keeps the corners honest at every viewport.
   Four stops rather than three so the falloff feathers instead of
   banding across the large flat wall behind it. */
.hm-plate[data-plate="inventory"] {
  --plate-scrim: 0.40;
  --plate-vignette: 0.96;
  --plate-vignette-inner: 24%;
}

.hm-grain {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: var(--grain);
  background-repeat: repeat;
  opacity: 0.03;
  mix-blend-mode: overlay;
}

.hm-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  z-index: 60;
  background: linear-gradient(90deg, var(--gold-accent), var(--color-primary), var(--color-amber-accent));
  transform: scaleX(var(--progress, 0));
  transform-origin: 0 50%;
  pointer-events: none;
  opacity: 0.85;
}

.hm-flow { position: relative; z-index: 20; }

.hm-inner {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.hm-solid { background-color: var(--color-surface); }

/* ── Shared type ─────────────────────────────────────────── */
.hm-label {
  display: block;
  color: var(--color-primary);
  font-family: var(--font-label);
  font-size: 0.625rem;
  letter-spacing: 0.38em;
  text-transform: uppercase;
  margin-bottom: 0.9rem;
}
.hm-label::after {
  content: '';
  display: block;
  width: 0;
  height: 1px;
  margin-top: 0.7rem;
  background: linear-gradient(90deg, var(--color-primary), transparent);
  transition: width 1.4s var(--ease-out-soft) 0.2s;
}
.reveal-on-scroll.visible .hm-label::after { width: 4.5rem; }

.hm-title {
  font-family: var(--font-headline);
  font-size: clamp(1.85rem, 7vw, 3.6rem);
  line-height: 1.1;
  letter-spacing: -0.022em;
  margin-bottom: 0.9rem;
}

.hm-desc {
  color: var(--color-on-surface-variant);
  font-size: 1rem;
  line-height: 1.78;
  max-width: 36rem;
}
.hm-desc + .hm-desc { margin-top: 0.9rem; }

/* ── FOLD 1 — HERO ────────────────────────────────────────
   The plate settles from scale 1.1 to 1 over ~3.4s once
   main.js adds body.page-loaded, so the room appears to come
   to rest as the title resolves. This is the one place a plate
   transforms, and it happens on load only — never on scroll. */
.hm-hero {
  position: relative;
  /* Same as .ss-hero: subtract the breadcrumb band so the hero — and the
     .hm-scroll-cue pinned to its bottom edge — still end at the fold. */
  min-height: calc(100svh - var(--gt-bc-height));
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: calc(var(--gt-bc-header) + 1.5rem) var(--gutter) 7rem;
}
.hm-hero::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 22svh;
  background: linear-gradient(to bottom, transparent, var(--color-surface));
  pointer-events: none;
}

.hm-plate[data-plate="hero"] { transform: translateZ(0) scale(1.1); }
body.page-loaded .hm-plate[data-plate="hero"] {
  transition: opacity 1.3s var(--ease-out-soft), transform 3.4s var(--ease-out-soft);
  transform: translateZ(0) scale(1);
}

.hm-hero-eyebrow {
  display: inline-block;
  color: var(--color-primary);
  font-family: var(--font-label);
  font-size: 0.5625rem;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  padding: 0.5rem 1.05rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: rgba(10, 7, 6, 0.42);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  margin-bottom: 1.5rem;
}

.hm-hero-title {
  font-family: var(--font-headline);
  font-size: clamp(2.4rem, 12vw, 5.75rem);
  font-weight: 700;
  line-height: 1.03;
  letter-spacing: -0.035em;
  text-shadow: 0 6px 52px rgba(0, 0, 0, 0.7);
  margin-bottom: 1rem;
}

.hm-hero-desc {
  max-width: 36rem;
  margin: 0 auto;
  color: rgba(237, 224, 220, 0.86);
  font-size: clamp(0.95rem, 3.6vw, 1.2rem);
  line-height: 1.76;
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.78);
}

.hm-scroll-cue {
  position: absolute;
  left: 50%;
  bottom: max(2.25rem, env(safe-area-inset-bottom));
  color: var(--color-primary);
  opacity: 0.65;
  z-index: 2;
}

/* ── FOLD 2 — CLIMATE MATRIX ──────────────────────────────
   Numbers count up from zero when the row enters view
   (immersive.js, [data-count-to]). The suffix is a separate
   span so only the digits animate. */
.hm-climate {
  padding: clamp(3.5rem, 11svh, 8rem) 0;
  text-align: center;
}
.hm-climate-head { max-width: 40rem; margin: 0 auto clamp(2rem, 5svh, 3.5rem); }
.hm-climate-head .hm-desc { margin-inline: auto; }
.hm-climate-head .hm-label::after { margin-inline: auto; }

.hm-stats {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
  max-width: 60rem;
  margin: 0 auto;
  padding-top: clamp(1.5rem, 4svh, 2.75rem);
  border-top: 1px solid rgba(85, 67, 54, 0.24);
}

.hm-stat-value {
  display: block;
  font-family: var(--font-headline);
  font-size: clamp(2.5rem, 13vw, 3.75rem);
  line-height: 1;
  color: var(--color-on-surface);
  filter: drop-shadow(0 0 14px rgba(217, 119, 7, 0.34));
  margin-bottom: 0.5rem;
  /* Tabular figures stop the row twitching as digit widths change
     during the count-up. */
  font-variant-numeric: tabular-nums;
}
.hm-stat-value span { color: var(--color-primary); }

.hm-stat-label {
  display: block;
  font-family: var(--font-label);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-on-surface-variant);
  margin-bottom: 0.4rem;
}
.hm-stat-note {
  font-size: 0.8125rem;
  line-height: 1.6;
  color: rgba(219, 194, 176, 0.6);
  max-width: 17rem;
  margin: 0 auto;
}

/* ── FOLD 3 — INVENTORY BREAK ─────────────────────────────
   Only a top fade. The bottom hand-off is owned by the
   gallery's feather so the plate can bleed continuously from
   this section into the next one. */
.hm-break {
  position: relative;
  min-height: 70svh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: clamp(3.5rem, 11svh, 8rem) var(--gutter);
}
.hm-break::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 18svh;
  background: linear-gradient(to bottom, var(--color-surface), transparent);
  pointer-events: none;
}

.hm-break-quote {
  position: relative;
  max-width: 44rem;
  font-family: var(--font-headline);
  font-size: clamp(1.35rem, 5.6vw, 2.5rem);
  line-height: 1.34;
  letter-spacing: -0.015em;
  text-shadow: 0 4px 44px rgba(0, 0, 0, 0.9);
}
.hm-break-attr {
  display: block;
  margin-top: 1.5rem;
  font-family: var(--font-label);
  font-size: 0.5625rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--color-primary);
}

/* ── FOLD 4 — GALLERY ─────────────────────────────────────
   The feather. No hard edge where this section starts: the fixed
   plate from the fold above carries through the top and dissolves
   as the tiles arrive. The tall top padding is what buys room
   for it.

   That dissolve used to be a gradient on this element's own
   background. It is now a mask on ::before — same alpha curve,
   but the thing being dissolved into is a photographic bed
   rather than a flat colour. See ::before below. */
.hm-gallery {
  position: relative;
  /* Top padding is the bed's open run: the feather completes at
     22svh, so everything past that is clear photograph before the
     heading arrives. Raised from 24svh to 36svh to widen that gap
     — the feather is pinned in svh and does not grow with it, so
     the extra height lands entirely on the visible side. */
  padding: 36svh 0 clamp(5rem, 14svh, 8rem);
}

/* The bed. Lives on ::before rather than as another background
   layer on the section, because background layers share a single
   mask — and masking the section would mask the tiles along with
   it.

   The restraint is the design, not a compromise. This fold already
   carries five photographs; the tiles are the subject. A sixth
   photograph competing at full strength reads as noise, not
   luxury. So the bed is held near a quarter strength — enough to
   register as depth and material in the gaps between tiles, never
   enough to argue with them. Measured, the grid band sits at 0.154
   mean against the tiles' own lit interiors, and the heading band
   at 0.145. */
.hm-gallery::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Base bed, and the fallback if the photograph never arrives. */
  background-color: var(--color-surface-container-lowest);
  background-image:
    /* 3 — vignette, weighted high so the eye settles on the
       heading before the grid pulls it down */
    radial-gradient(ellipse 120% 68% at 50% 24%,
      transparent 22%,
      rgba(8, 6, 5, 0.34) 60%,
      rgba(8, 6, 5, 0.60) 100%),
    /* 2 — scrim. Opens slightly at the top so the bed has a moment
       of arrival as the feather completes, settles to its hold
       value through the grid, then closes to solid so the fold
       hands off into .hm-ritual with no seam. */
    linear-gradient(to bottom,
      rgba(19, 13, 11, 0.58) 0%,
      rgba(19, 13, 11, 0.76) 40%,
      rgba(19, 13, 11, 0.76) 90%,
      var(--color-surface) 100%),
    /* 1 — the photograph */
    var(--plate-shelf);
  background-size: auto, auto, cover;
  background-position: center, center, center 35%;
  background-repeat: no-repeat;
  /* The feather, reproduced exactly: transparent → 0.55 at 12svh →
     solid at 22svh. Same alpha curve the section background used
     before this bed existed, so the plate hand-off is unchanged. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, rgba(0, 0, 0, 0.55) 12svh, #000 22svh);
          mask-image: linear-gradient(to bottom, transparent 0, rgba(0, 0, 0, 0.55) 12svh, #000 22svh);
}

/* ::before is absolutely positioned, so it would otherwise paint
   over the in-flow heading and grid. */
.hm-gallery .hm-inner { position: relative; }
.hm-gallery-head { text-align: center; max-width: 40rem; margin: 0 auto clamp(1.75rem, 4svh, 3rem); }
.hm-gallery-head .hm-desc { margin-inline: auto; }
.hm-gallery-head .hm-label::after { margin-inline: auto; }

/* ── Gallery carousel ─────────────────────────────────────
   A native overflow-x + scroll-snap track. The browser owns
   swiping, momentum and snapping; immersive.js only auto-advances,
   syncs the dots and steps aside the moment someone touches it
   ([data-carousel], already loaded by this page).

   Two constraints are carried over from the smoke-shop carousel.
   Both are load-bearing and both were learned the hard way — the
   full post-mortems are in js/immersive.js and gator-tobacco.css:

     1. NO touch-action anywhere in this subtree. It is intersected
        across an element and ALL its ancestors, so pan-x here
        would forbid vertical page scrolling from anywhere inside
        the carousel — that was a total scroll lockout on mobile.
     2. NO will-change on a slide. Nothing animates a slide's
        transform, so the hint would promote and destroy one
        composited layer per card on every swipe for no benefit.
        That is what made the smoke-shop cards blank out on phones.

   scroll-snap-type is proximity, not mandatory: mandatory forces a
   full snap animation on every release, which reads as the
   carousel fighting the user rather than following them. */
.hm-gallery-carousel { position: relative; }

.hm-gallery-track {
  /* Load-bearing: makes the track the slides' offsetParent. The
     engine caches slide offsetLeft once and scrolls the track to
     those values, so if the offsets are measured against an outer
     wrapper instead, the dots advance but the track never moves. */
  position: relative;
  display: flex;
  gap: 0.9rem;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  overscroll-behavior-x: contain;
  /* Side padding lets the first and last slide centre when snapped. */
  padding: 0.5rem var(--gutter) 1.25rem;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.hm-gallery-track::-webkit-scrollbar { display: none; width: 0; height: 0; }

/* Sizing only — .hm-tile below still supplies the imagery, scrim
   and caption, so a slide is <figure class="hm-tile hm-slide">. */
.hm-slide {
  flex: 0 0 auto;
  width: min(78vw, 22rem);
  aspect-ratio: 4 / 3;
  scroll-snap-align: center;
}

.hm-tile {
  position: relative;
  overflow: hidden;
  border-radius: 0.85rem;
  border: 1px solid var(--rule);
  background-color: var(--color-surface-container-low);
}
.hm-tile::before {
  content: '';
  position: absolute;
  inset: -4%;
  background-size: cover;
  background-position: center;
  transition: transform 1.4s var(--ease-out-soft);
}
.hm-tile::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(5, 4, 3, 0.72), transparent 62%);
  opacity: 0.85;
  transition: opacity 0.8s var(--ease-out-soft);
}
@media (hover: hover) {
  .hm-tile:hover::before { transform: scale(1.09); }
  .hm-tile:hover::after  { opacity: 0.55; }
  .hm-tile:hover .hm-tile-caption { transform: translateY(0); opacity: 1; }
}
/* Touch equivalent of the hover lift — a press gives feedback. */
.hm-tile:active::before { transform: scale(1.05); }

.hm-tile[data-tile="inventory"]::before { background-image: var(--plate-inventory); }
.hm-tile[data-tile="cedar"]::before     { background-image: var(--plate-cedar); }
.hm-tile[data-tile="climate"]::before   { background-image: var(--plate-climate); }
.hm-tile[data-tile="ritual"]::before    { background-image: var(--plate-ritual); }
.hm-tile[data-tile="shelf"]::before     { background-image: var(--plate-shelf); }
.hm-tile[data-tile="shelf-2"]::before   { background-image: var(--plate-shelf-2); }

/* On a pointer device the cursor says which card you are on; on a
   touch screen nothing does. immersive.js marks the snapped slide
   .is-current so it can carry the same lit treatment hover gives. */
.hm-tile.is-current { border-color: rgba(255, 183, 125, 0.34); }
.hm-tile.is-current::after { opacity: 0.58; }
.hm-tile.is-current .hm-tile-caption { transform: translateY(0); opacity: 1; }

/* ── Reserved slots ───────────────────────────────────────
   Empty frames waiting on photography. Filling one is the same
   three steps every other tile already follows: add a --plate-<name>
   entry in .page-humidor, add a matching
   .hm-tile[data-tile="<name>"]::before rule beside the others, and
   change the slide's data-tile from slot-N to <name>. Kept
   deliberately quiet — a placeholder should read as a considered
   gap, not as a broken image. */
.hm-tile[data-tile^="slot-"] {
  border-style: dashed;
  border-color: rgba(197, 160, 89, 0.22);
  background-color: rgba(15, 11, 9, 0.55);
}
.hm-tile[data-tile^="slot-"]::before { background-image: none; }
.hm-tile[data-tile^="slot-"]::after { opacity: 0; }
.hm-slot-mark {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  color: rgba(219, 194, 176, 0.42);
}
.hm-slot-mark .material-symbols-outlined { font-size: 1.6rem; }
.hm-slot-label {
  font-family: var(--font-label);
  font-size: 0.5625rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
}

/* ── Carousel nav ─────────────────────────────────────────
   Amber, not the smoke-shop's neon — the two pages run different
   visual languages on purpose (see plates/SHOT-LIST.md). */
.hm-gallery-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  margin-top: 0.5rem;
}

.hm-gallery-dots { display: flex; align-items: center; gap: 0.5rem; }

.carousel-dot {
  width: 0.5rem;
  height: 0.5rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(197, 160, 89, 0.28);
  cursor: pointer;
  transition: background 0.4s ease, width 0.4s var(--ease-out-soft);
}
.carousel-dot.is-active { width: 1.5rem; background: var(--color-primary); }

.hm-gallery-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 999px;
  border: 1px solid var(--rule);
  background: rgba(24, 18, 16, 0.72);
  color: var(--color-on-surface);
  cursor: pointer;
  transition: transform 0.3s var(--ease-out-soft), border-color 0.4s ease, color 0.4s ease;
}
.hm-gallery-btn:active { transform: scale(0.94); }
@media (hover: hover) {
  .hm-gallery-btn:hover { border-color: rgba(255, 183, 125, 0.4); color: var(--color-primary); }
}

/* z-index lifts the caption above .hm-tile::after. In paint order ::after is
   the element's last child, so without this the scrim would render on top of
   the caption text and dim it. */
.hm-tile-caption {
  position: absolute;
  z-index: 2;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0.8rem 0.9rem;
  font-family: var(--font-label);
  font-size: 0.625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-on-surface);
  transform: translateY(0.4rem);
  opacity: 0.8;
  transition: transform 0.7s var(--ease-out-soft), opacity 0.7s var(--ease-out-soft);
}

/* ── FOLD 5 — THE RITUAL ──────────────────────────────────── */
.hm-ritual { padding: clamp(3.5rem, 10svh, 6rem) 0; }
.hm-ritual-head { max-width: 40rem; margin: 0 auto clamp(2rem, 5svh, 3rem); text-align: center; }
.hm-ritual-head .hm-desc { margin-inline: auto; }
.hm-ritual-head .hm-label::after { margin-inline: auto; }

.hm-steps {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.9rem;
  counter-reset: hm-step;
}

.hm-step {
  position: relative;
  isolation: isolate;
  padding: 1.4rem;
  border-radius: 1rem;
  border: 1px solid var(--rule);
  background: linear-gradient(158deg, rgba(37, 30, 28, 0.8), rgba(13, 11, 9, 0.9));
  overflow: hidden;
  transition: transform 0.7s var(--ease-out-soft),
              border-color 0.5s ease,
              box-shadow 0.7s var(--ease-out-soft);
}
.hm-step::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(20rem circle at var(--mx, 50%) var(--my, 0%), rgba(255, 183, 125, 0.14), transparent 62%);
  opacity: 0;
  transition: opacity 0.55s ease;
}
@media (hover: hover) {
  .hm-step:hover { transform: translateY(-5px); border-color: rgba(255, 183, 125, 0.32); box-shadow: 0 24px 56px -24px rgba(0, 0, 0, 0.95); }
  .hm-step:hover::before { opacity: 1; }
}

.hm-step-num {
  counter-increment: hm-step;
  display: block;
  font-family: var(--font-headline);
  font-size: 0.8125rem;
  letter-spacing: 0.2em;
  color: var(--color-primary);
  margin-bottom: 0.75rem;
}
.hm-step-num::before { content: '0' counter(hm-step); }

.hm-step-title { font-family: var(--font-headline); font-size: 1.15rem; line-height: 1.3; margin-bottom: 0.5rem; }
.hm-step-desc  { color: var(--color-on-surface-variant); font-size: 0.9rem; line-height: 1.7; }

/* ── FOLD 6 — CTA ─────────────────────────────────────────── */
.hm-cta {
  position: relative;
  padding: clamp(3.5rem, 10svh, 6rem) 0 clamp(6rem, 14svh, 8rem);
  text-align: center;
  background-image: var(--plate-inventory);
  background-size: cover;
  background-position: center;
}
.hm-cta::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, var(--color-surface), rgba(13, 11, 9, 0.88) 42%, var(--color-surface));
}
.hm-cta > * { position: relative; }

.hm-cta-actions {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  align-items: stretch;
  margin-top: 1.75rem;
}
.hm-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 3rem;
  font-family: var(--font-label);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.9rem 1.5rem;
  border-radius: 0.55rem;
  border: 1px solid var(--rule);
  color: var(--color-on-surface);
  background: rgba(24, 18, 16, 0.78);
  transition: transform 0.3s var(--ease-out-soft), background 0.4s ease,
              border-color 0.4s ease, color 0.4s ease;
}
/* order:-1 pulls the icon back to the visual left. The label has to come
   first in the DOM so Google Sitelinks never reads the ligature word as the
   link's text — see the Sitelinks Isolation note in CLAUDE.md. */
.hm-btn .material-symbols-outlined { font-size: 1.1rem; order: -1; }
.hm-btn:active { transform: scale(0.98); }
.hm-btn--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
  border-color: transparent;
  font-weight: 800;
}
@media (hover: hover) {
  .hm-btn:hover { transform: translateY(-2px); border-color: rgba(255, 183, 125, 0.4); color: var(--color-primary); }
  .hm-btn--primary:hover { background: var(--color-primary-fixed); color: var(--color-on-primary); }
}

/* ══════════════════════════════════════════════════════════
   Breakpoints — mobile-first, so everything above is portrait
   ══════════════════════════════════════════════════════════ */
@media (min-width: 560px) {
  .hm-cta-actions { flex-direction: row; flex-wrap: wrap; justify-content: center; }
  .hm-btn { flex: 0 1 auto; }
  .hm-slide { width: min(64vw, 24rem); }
}

@media (min-width: 768px) {
  /* .hm-hero padding-top override removed — --gt-bc-header already tracks the
     real header height across the 1024px breakpoint. */
  .hm-stats { grid-template-columns: repeat(3, 1fr); gap: 2.5rem; }
  .hm-steps { grid-template-columns: repeat(2, 1fr); }
  .hm-slide { width: 26rem; }
  .hm-desc { font-size: 1.0625rem; }
  .hm-step { padding: 1.75rem; }
}

@media (min-width: 1080px) {
  .hm-steps { grid-template-columns: repeat(4, 1fr); }
  .hm-slide { width: 30rem; }
  /* Keep the track from running edge to edge on wide desktops, so
     the first and last slide still centre against the content. */
  .hm-gallery-track { padding-inline: max(var(--gutter), calc((100vw - 72rem) / 2)); }
}

/* ── Reduced motion ───────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .hm-plate[data-plate="hero"],
  body.page-loaded .hm-plate[data-plate="hero"] { transform: translateZ(0); transition: opacity 0.3s linear; }
  .hm-tile::before { transition: none; }
  .hm-tile:hover::before,
  .hm-tile:active::before { transform: none; }
  .hm-step { transition: border-color 0.3s ease; }
  .hm-step:hover { transform: none; }
  .hm-label::after { transition: none; width: 4.5rem; }
}
