/* VSLocks landing page presentation. Loaded by front-page.php. */

:root {
  color-scheme: dark;
  --bg: #000000;
  --brand-red: #e8000d;
  --brand-grey: #deddde;
  --text: #f6f5f6;
  --muted: rgba(255, 255, 255, 0.86);
  --soft: rgba(255, 255, 255, 0.6);
  --line: rgba(255, 255, 255, 0.18);
  --card: rgba(255, 255, 255, 0.035);
  --shadow: rgba(0, 0, 0, 0.56);
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* Mandatory snap — scrolling never stops in between scenes. The
     earlier tap-to-snap bug that made us switch to proximity is no
     longer an issue because the fluid-smoke canvas touch listeners
     are now passive (no preventDefault), so taps don't register as
     scroll jitter. */
  scroll-snap-type: y mandatory;
}

body {
  margin: 0;
  min-height: 100vh;
  overflow-x: hidden;
  background: #020202;
  color: var(--text);
  font-family: "arpona", serif;
}

main {
  position: relative;
}

.viewport-shell {
  position: sticky;
  top: 0;
  z-index: 1;
  height: 100svh;
  overflow: hidden;
  background: #030303;
  display: grid;
  /* R4 B9 (Adeel Apr 23): compositor hint so the sticky→unsticky
     handoff at the testimonials boundary doesn't stutter. */
  will-change: transform;
  /* SWG-27 v7 REVERTED (Playwright video showed hard visible cut
     between scene 6 partial-translate and testimonials underneath).
     The desktop --shell-exit transform fought with the natural
     sticky-position behavior, producing a broken double-translate.
     Mobile keeps its own --shell-exit in its @media block.
     Desktop: scroll-snap handles the transition natively. */
}

.stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #020202;
}

.stage::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.24), rgba(0, 0, 0, 0.52) 68%, rgba(0, 0, 0, 0.86)),
    linear-gradient(90deg, rgba(0, 0, 0, 0.82) 0%, rgba(0, 0, 0, 0.34) 32%, rgba(0, 0, 0, 0.28) 58%, rgba(0, 0, 0, 0.78) 100%);
}

/* SWG-27 v8: when active scene is testimonials, the stage holds
   structured text content (not a video). The depth-darkening
   ::before overlay made for video scenes was washing the text out.
   Disable it on testimonials. */
.stage[data-scene="testimonials"]::before {
  display: none;
}

.stage[data-scene="1"]::before {
  display: none;
}

.media-stack {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  transform: translate3d(0, calc(var(--scene-offset, 0) * -100svh), 0);
  transition: transform 1100ms cubic-bezier(0.16, 1, 0.3, 1);
  will-change: transform;
}

.stage-layer {
  position: relative;
  width: 100%;
  height: 100svh;
  flex: 0 0 100svh;
  object-fit: cover;
  object-position: center;
  filter: grayscale(1) contrast(1.08) brightness(0.68);
}

/* SWG-27 v8: testimonials stage-layer. Same dimensions / flex behaviour
   as a video stage-layer, but with structured content (cards + header)
   instead of a background video. Filter override clears the grayscale
   + brightness treatment that applies to scene videos. Pointer-events
   re-enabled so testimonial cards can be interacted with. */
.stage-layer-testimonials {
  background: #0a0a0a;
  color: #ffffff;
  padding: clamp(80px, 14vh, 140px) clamp(32px, 8vw, 120px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  filter: none;
  pointer-events: auto;
  overflow: hidden;
}

.stage-panel {
  overflow: hidden;
  background: #050505;
  filter: grayscale(1) contrast(1.08) brightness(0.68);
}

.stage-panel iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  pointer-events: none;
}

.stage-layer[data-scene-layer="1"] {
  /* R4 B1 / SWG-19 (Adeel May 2): on his 3440×1440 ultrawide,
     `contain` left bars on the sides because the source video is
     16:9 in a 21:9 viewport. Switched to `cover` so video fills
     the full screen edge-to-edge across any monitor aspect.
     Mild top/bottom crop on ultrawide (~17% each side); on a
     standard 16:9 it's a perfect fit with no crop at all.
     object-position: 54% center keeps the lock visible after
     the crop. */
  object-fit: cover;
  object-position: 54% center;
  background: #000;
  filter: none;
}

.stage[data-scene="2c"]::before,
.stage[data-scene="3c"]::before,
.stage[data-scene="3d"]::before {
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.18) 72%, rgba(0, 0, 0, 0.36)),
    linear-gradient(90deg, rgba(0, 0, 0, 0.18) 0%, rgba(0, 0, 0, 0.04) 24%, rgba(0, 0, 0, 0.04) 76%, rgba(0, 0, 0, 0.18) 100%);
}

/* Scenes 3c and 3d — cropped / bokeh welding clips. We compressed
   them aggressively (CRF 28-30, 720p base) so a gentle blur hides
   compression banding without killing the composition. Filter
   chain has to include the base grayscale/contrast/brightness from
   .stage-layer-video (line ~123) so we don't lose the B&W treatment. */
.stage-layer[data-scene-layer="3c"] {
  /* SWG-71 (Dwi May 12): new AI-generated `welding-spark-sparks-only.mp4`
     replaces the old cropped/upscaled `construction-industrial.mp4`.
     No bright center light, sparks only.
     The welder + sparks sit in the bottom-right ~15% of the source
     frame. On 16:9 desktop / ultrawide, full frame is visible so
     the welder appears bottom-right naturally. On mobile portrait
     with object-fit: cover, only ~25% of source width is visible;
     anchoring object-position to the right edge keeps the welder
     in frame instead of cropping to the empty central structural
     steel. CSS blur dropped (clean AI source, no compression banding). */
  object-fit: cover;
  object-position: 90% 70%;
  filter: grayscale(1) contrast(1.08) brightness(0.68);
}

/* Scene 3d removed per SWG-75 (Adeel May 6): "spark dots are too
   thick, they're like balls... that's why I feel that this is not
   accurate representation." Scene 3c retained as the welding scene. */

/* Scene 4 — Frost. Restored R4 B7 (Adeel Apr 23) — pexels still that
   was live at the moment of Round 3 deletion. */
.stage-layer[data-scene-layer="4"] {
  object-position: center center;
}

.stage-layer[data-scene-layer="5"] {
  object-position: 48% center;
}

.stage-layer[data-scene-layer="6"] {
  object-position: 48% center;
}

.fx-canvas {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* === Global fluid smoke overlay — red WebGL smoke on all scroll pages === */
/* mix-blend-mode: screen makes the black iframe background invisible;
   only the red smoke is composited over the scene. */
.fluid-smoke-overlay {
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 400ms ease;
  mix-blend-mode: screen;
}

.fluid-smoke-overlay.is-active.is-ready {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

/* Mobile/touch: overlay still receives touch for smoke reactivity
   (taps and horizontal drags drive the fluid sim), but vertical
   pan gestures are handed off to the browser for page scroll.
   `touch-action: pan-y` splits the responsibility at the gesture
   level — vertical swipes scroll, everything else hits the iframe. */
@media (hover: none), (pointer: coarse) {
  .fluid-smoke-overlay.is-active {
    pointer-events: auto;
    touch-action: pan-y;
  }
  .fluid-smoke-overlay.is-active iframe {
    touch-action: pan-y;
  }
}

.fluid-smoke-overlay iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  background: #000;
  color-scheme: dark;
}

.hero-logo {
  position: absolute;
  top: clamp(20px, 4.2vh, 46px);
  left: clamp(24px, 4vw, 52px);
  z-index: 5;
  width: min(24vw, 280px);
  max-width: calc(100vw - 84px);
  opacity: 0;
  transform: translate3d(0, -10px, 0);
  transition:
    opacity 260ms ease,
    transform 260ms ease;
  pointer-events: none;
  filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.26));
}

.hero-logo.is-visible {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

.hero-logo img {
  display: block;
  width: 100%;
  height: auto;
}

/* SWG-73 (Adeel May 6): cart + search icons + login pill.
   Desktop: right-aligned cluster (search → cart → hamburger).
   Mobile (SWG-88): left cluster (search + cart), logo centered, hamburger right.
   The original "hide on mobile" rule (Imam May 11) was reversed by Adeel R5 2026-05-21. */
.mobile-utility-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: fixed;
  top: clamp(14px, 3svh, 22px);
  z-index: 21;
  width: 44px;
  height: 44px;
  padding: 0;
  background: rgba(0, 0, 0, 0.42);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  color: #fff;
  backdrop-filter: blur(10px);
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 200ms ease, border-color 200ms ease;
}
.mobile-utility-icon:active,
.mobile-utility-icon:hover {
  background: rgba(232, 0, 13, 0.6);
  border-color: rgba(232, 0, 13, 0.6);
}

/* Hide top-bar utility surface (search, cart, login pill) when the
   hamburger panel is open. Otherwise they paint over the panel
   content on desktop where the panel only covers the right ~33vw. */
body.menu-open .mobile-utility-icon,
body.menu-open .utility-bar {
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms ease;
}

/* Desktop right-cluster (right to left): hamburger → cart → search.
   44px buttons, 12px gaps. Login is INSIDE the hamburger panel
   only, no standalone pill. */
.mobile-utility-icon-cart {
  right: calc(clamp(14px, 4vw, 22px) + 44px + 12px);
}
.mobile-utility-icon-search {
  right: calc(clamp(14px, 4vw, 22px) + (44px + 12px) * 2);
}

/* SWG-69 (Adeel May 6): hero centered text overlay, Rolex-style.
   "Bigger and one line, like Rolex... maybe the big text is right
   in one line, and the smaller one at the bottom." Plus a CTA
   button below per "(call to action) Yeah. Definitely." Visible
   only on scene 1, toggled by JS like .hero-logo. */
.hero-centered {
  /* SWG-69 v2 (Adeel May 6 follow-up): horizontally centered but
     anchored to the lower portion of the viewport, matching Rolex.
     Not vertically centered. Sits around the lower-third area. */
  position: absolute;
  bottom: 16vh;
  left: 50%;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(14px, 2vh, 22px);
  width: min(86vw, 880px);
  opacity: 0;
  transform: translate3d(-50%, 12px, 0);
  transition: opacity 320ms ease, transform 320ms ease;
  pointer-events: none;
}
.hero-centered.is-visible {
  opacity: 1;
  transform: translate3d(-50%, 0, 0);
  pointer-events: auto;
}
.hero-centered-title {
  margin: 0;
  font-family: "ethnocentric", sans-serif;
  /* SWG-69 (Adeel May 13): "A BRAND OF TRUST" on a single line.
     Font dropped ~2 levels (was clamp(2.4rem, 5.4vw, 5.2rem)) and
     white-space: nowrap so it never wraps. max-width removed so the
     single line can use the full hero width. */
  font-size: clamp(1.4rem, 4.6vw, 3.4rem);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: 0.01em;
  white-space: nowrap;
  color: #ffffff;
  text-shadow: 0 6px 22px rgba(0, 0, 0, 0.45);
}
.hero-centered-subline {
  margin: 0;
  font-family: "arpona", serif;
  font-size: clamp(1rem, 1.4vw, 1.2rem);
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
  max-width: 32ch;
}
.hero-centered-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 52px;
  padding: 0 28px;
  margin-top: clamp(8px, 1.4vh, 14px);
  background: rgba(232, 0, 13, 0.92);
  border: 1px solid rgba(232, 0, 13, 0.92);
  border-radius: 999px;
  color: #ffffff;
  font-family: "arpona", serif;
  font-size: 0.94rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-decoration: none;
  transition: background-color 200ms ease, transform 160ms ease;
}
.hero-centered-cta:hover {
  background: #c40010;
}
.hero-centered-cta:active {
  background: #c40010;
  transform: translateY(1px);
}

@media (prefers-reduced-motion: reduce) {
  .hero-centered {
    transition: opacity 200ms ease;
    transform: translate3d(-50%, 0, 0);
  }
}

.utility-bar {
  /* SWG-73 desktop: login pill sits between the cart icon and the
     hamburger button. Top baseline matches the hamburger. */
  position: fixed;
  top: clamp(14px, 3svh, 22px);
  right: calc(clamp(14px, 4vw, 22px) + (44px + 12px) * 1);
  z-index: 21;
  display: flex;
  gap: 12px;
  height: 44px;
  align-items: center;
  /* Let cursor/touch pass through the bar so smoke reacts everywhere
     except the actual Login button. */
  pointer-events: none;
}

.utility-link {
  pointer-events: auto; /* re-enable only on the actual link */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 108px;
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.42);
  color: var(--text);
  font-family: "arpona", serif;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-decoration: none;
  backdrop-filter: blur(10px);
  transition: background-color 200ms ease, border-color 200ms ease, box-shadow 200ms ease, transform 200ms ease;
}

.utility-link.is-accent {
  border-color: rgba(232, 0, 13, 0.38);
  color: #ffffff;
  box-shadow: 0 0 0 1px rgba(232, 0, 13, 0.1) inset;
}

.utility-link.is-accent:hover,
.utility-link.is-accent:focus-visible {
  background: #e8000d;
  border-color: #e8000d;
  box-shadow: 0 0 0 1px rgba(232, 0, 13, 0.6) inset, 0 8px 24px -10px rgba(232, 0, 13, 0.6);
}

.utility-link.is-accent:active {
  background: #c40010;
  border-color: #c40010;
  transform: translateY(1px);
}

/* ============================================================
   HAMBURGER MENU
   R4 B10 (mobile, Apr 24) + SWG-65 (desktop, Adeel May 2):
   "maybe you can add this on desktop too." Top-right fixed
   icon, taps to slide in a right-side panel with Login inside.
   Originally mobile-only inside `@media (max-width: 880px)`,
   now applies at all breakpoints. Mobile narrows the panel via
   the override at the bottom of this section.
   ============================================================ */
  .hamburger-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    top: clamp(14px, 3svh, 22px);
    /* Top-right corner — keeps the hero-logo (top-left, scene 1)
       clear, and reuses the spot the standalone Login pill used
       to occupy before Login moved into the hamburger panel. */
    right: clamp(14px, 4vw, 22px);
    /* Above the slide-in panel (z:20) and backdrop (z:19) so the
       icon morphs to X over the open panel and stays tappable. */
    z-index: 21;
    width: 44px;
    height: 44px;
    padding: 0;
    background: rgba(0, 0, 0, 0.42);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 999px;
    color: #fff;
    backdrop-filter: blur(10px);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color 200ms ease, border-color 200ms ease;
  }
  .hamburger-btn:active {
    background: rgba(232, 0, 13, 0.6);
    border-color: rgba(232, 0, 13, 0.6);
  }
  .hamburger-btn .lines {
    position: relative;
    display: block;
    width: 18px;
    height: 14px;
  }
  .hamburger-btn .lines span {
    position: absolute;
    left: 0;
    right: 0;
    height: 2px;
    background: #fff;
    border-radius: 2px;
    transition: transform 220ms ease, top 220ms ease, opacity 180ms ease;
  }
  .hamburger-btn .lines span:nth-child(1) { top: 0; }
  .hamburger-btn .lines span:nth-child(2) { top: 6px; }
  .hamburger-btn .lines span:nth-child(3) { top: 12px; }

  /* Open state — morph to X */
  body.menu-open .hamburger-btn .lines span:nth-child(1) {
    top: 6px;
    transform: rotate(45deg);
  }
  body.menu-open .hamburger-btn .lines span:nth-child(2) {
    opacity: 0;
  }
  body.menu-open .hamburger-btn .lines span:nth-child(3) {
    top: 6px;
    transform: rotate(-45deg);
  }

  .hamburger-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 19;
    background: rgba(0, 0, 0, 0.55);
    opacity: 0;
    pointer-events: none;
    transition: opacity 240ms ease;
  }
  body.menu-open .hamburger-backdrop {
    opacity: 1;
    pointer-events: auto;
  }

  .hamburger-panel {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 20;
    width: min(33vw, 480px);
    padding: clamp(72px, 14svh, 96px) 28px clamp(28px, 5svh, 40px) 28px;
    background: #050505;
    border-left: 1px solid rgba(255, 255, 255, 0.06);
    /* Slide in from the right to match the top-right hamburger
       button position. Standard mobile pattern. */
    transform: translate3d(100%, 0, 0);
    transition: transform 280ms cubic-bezier(0.32, 0.72, 0, 1);
    will-change: transform;
    /* Nested category menu can run taller than the viewport on
       phones. Let the panel scroll its own content; contain the
       scroll so it never chains to the page behind. */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }
  body.menu-open .hamburger-panel {
    transform: translate3d(0, 0, 0);
  }

  /* Panel brand: pin to the same top + height as the close (X)
     button so they share the top-bar baseline visually inside
     the open panel. Padding-top on the panel gives nav room
     below this absolute brand. */
  .hamburger-panel .panel-brand {
    position: absolute;
    top: clamp(14px, 3svh, 22px);
    left: 28px;
    height: 44px;
    display: flex;
    align-items: center;
    line-height: 0;
  }
  .hamburger-panel .panel-brand img {
    display: block;
    height: 100%;
    width: auto;
  }
  .hamburger-panel nav {
    display: flex;
    flex-direction: column;
    gap: 4px;
    /* Guarantee a gap above the Login CTA even when the category
       accordion expands and content overflows (margin-top:auto on
       .panel-cta collapses to 0 with no free space). */
    margin-bottom: 24px;
  }
  .hamburger-panel nav a {
    display: block;
    padding: 14px 0;
    font-family: "arpona", serif;
    font-size: 1.08rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: rgba(255, 255, 255, 0.92);
    text-decoration: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    transition: color 160ms ease;
  }
  .hamburger-panel nav a:active {
    color: var(--brand-red);
  }
  .hamburger-panel nav a:hover,
  .hamburger-panel nav a:focus-visible {
    color: var(--brand-red);
  }

  /* Products submenu is collapsed whenever the drawer opens. */
  .hamburger-panel nav .panel-submenu {
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  }
  .hamburger-panel nav .panel-submenu-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }
  .hamburger-panel nav .panel-submenu-heading {
    flex: 1 1 auto;
    padding: 14px 0;
    font-family: "arpona", serif;
    font-size: 1.02rem;
    font-weight: 400;
    letter-spacing: 0.04em;
    color: rgba(255, 255, 255, 0.96);
    text-decoration: none;
    text-transform: uppercase;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    transition: color 160ms ease;
  }
  .hamburger-panel nav .panel-submenu-toggle {
    flex: 0 0 auto;
    position: relative;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.08);
    color: rgba(255, 255, 255, 0.82);
    cursor: pointer;
  }
  .hamburger-panel nav .panel-submenu-toggle::before {
    content: "";
    position: absolute;
    inset: 0;
    width: 8px;
    height: 8px;
    margin: auto;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    transition: transform 160ms ease;
  }
  .hamburger-panel nav .panel-submenu.is-open .panel-submenu-toggle::before {
    transform: rotate(-135deg);
  }
  .hamburger-panel nav .panel-submenu-heading:active,
  .hamburger-panel nav .panel-submenu-heading:hover {
    color: var(--brand-red);
  }
  .hamburger-panel nav .panel-submenu:not(.is-open) .panel-submenu-items {
    display: none;
  }
  .hamburger-panel nav .panel-submenu .panel-submenu-items {
    display: flex;
    flex-direction: column;
    padding: 0 0 10px 14px;
  }
  .hamburger-panel nav .panel-submenu .panel-submenu-items a {
    padding: 10px 0;
    font-size: 0.98rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.76);
    border-bottom: none;
  }
  .hamburger-panel nav .panel-submenu .panel-submenu-items a:active {
    color: var(--brand-red);
  }
  .hamburger-panel nav .panel-submenu .panel-submenu-items a:hover,
  .hamburger-panel nav .panel-submenu .panel-submenu-items a:focus-visible {
    color: var(--brand-red) !important;
  }

  /* Product category tree inside the mobile navigation. */
  .hamburger-panel nav .panel-subcategory {
    border-bottom: none;
  }
  .hamburger-panel nav .panel-subcategory-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 0;
  }
  .hamburger-panel nav .panel-subcategory-parent {
    flex: 1 1 auto;
    padding: 0;
    border-bottom: 0;
    font-size: 0.98rem;
    color: rgba(255, 255, 255, 0.92);
    text-decoration: none;
  }
  .hamburger-panel nav .panel-subcategory-parent:hover,
  .hamburger-panel nav .panel-subcategory-parent:active {
    color: var(--brand-red);
  }
  .hamburger-panel nav .panel-subcategory-toggle {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.08);
    color: rgba(255, 255, 255, 0.82);
    cursor: pointer;
    position: relative;
  }
  .hamburger-panel nav .panel-subcategory-toggle::before {
    content: "";
    position: absolute;
    inset: 0;
    margin: auto;
    width: 8px;
    height: 8px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    transition: transform 160ms ease;
  }
  .hamburger-panel nav .panel-subcategory.is-open .panel-subcategory-toggle::before {
    transform: rotate(-135deg);
  }
  .hamburger-panel nav .panel-soon-link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    text-decoration: none;
    color: rgba(255, 255, 255, 0.72);
  }
  .hamburger-panel nav .panel-soon-link:active {
    color: var(--brand-red);
  }
  .hamburger-panel nav .panel-subcategory:not(.is-open) .panel-subcategory-items {
    display: none;
  }
  .hamburger-panel nav .panel-subcategory .panel-subcategory-items {
    display: flex;
    flex-direction: column;
    padding: 0 0 8px 14px;
  }
  .hamburger-panel nav .panel-subcategory .panel-subcategory-items a {
    padding: 8px 0;
    font-size: 0.92rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.72);
    border-bottom: none;
  }
  .hamburger-panel nav .panel-subcategory .panel-subcategory-items a:active {
    color: var(--brand-red);
  }
  .hamburger-panel nav .panel-subcategory .panel-subcategory-items a:hover,
  .hamburger-panel nav .panel-subcategory .panel-subcategory-items a:focus-visible {
    color: var(--brand-red) !important;
  }
  .hamburger-panel nav .panel-soon {
    padding: 8px 0;
    font-family: "arpona", serif;
    font-size: 0.92rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.34);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
  }
  .hamburger-panel nav > .panel-soon {
    padding: 14px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
  }
  .hamburger-panel nav .panel-soon .panel-soon-title {
    font-family: "arpona", serif;
    font-variant-ligatures: none;
    font-feature-settings: "liga" 0, "calt" 0;
  }
  .hamburger-panel nav .panel-soon em {
    font-style: normal;
    font-size: 0.66rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.42);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 999px;
    padding: 2px 8px;
    white-space: nowrap;
  }
  @media (max-width: 767.98px) {
    .hamburger-panel nav .panel-soon {
      flex-direction: column;
      align-items: flex-start;
      gap: 4px;
    }
    .hamburger-panel nav .panel-soon .panel-soon-badge {
      margin-left: 0;
    }
  }
  .hamburger-panel .panel-foot {
    margin-top: 18px;
    padding-top: 18px;
    font-family: "arpona", serif;
    font-size: 0.78rem;
    letter-spacing: 0.06em;
    color: rgba(255, 255, 255, 0.36);
  }

  /* Login / Sign Up moved INTO the panel as a primary CTA.
     Standalone .utility-bar is hidden on mobile (see below).
     Same red-accent treatment as the desktop pill. */
  .hamburger-panel .panel-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-top: auto;
    /* Never let flexbox compress the button when the panel
       content overflows (default flex-shrink:1 was squashing it
       as categories expand). */
    flex: 0 0 auto;
    height: 48px;
    min-height: 48px;
    padding: 0 22px;
    background: rgba(232, 0, 13, 0.92);
    border: 1px solid rgba(232, 0, 13, 0.92);
    border-radius: 999px;
    color: #ffffff;
    font-family: "arpona", serif;
    font-size: 0.96rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-decoration: none;
    transition: background-color 200ms ease, transform 160ms ease;
  }
  .hamburger-panel .panel-cta:active {
    background: #c40010;
    transform: translateY(1px);
  }

  /* Login lives inside the hamburger panel only. Standalone pill
     (.utility-bar) hidden everywhere. Per Imam May 11: do not
     surface Login outside the hamburger on either breakpoint. */
  .utility-bar { display: none !important; }

  /* When the menu is open, lock body scroll so the user doesn't
     accidentally swipe-snap a scene behind the open panel. */
  body.menu-open {
    overflow: hidden !important;
  }

/* SWG-65 mobile override: narrow the panel to a phone-friendly
   width and keep the original mobile gutter. Desktop default
   above (33vw / 480px max) feels too wide on portrait phones. */
@media (max-width: 880px) {
  .hamburger-panel {
    width: min(82vw, 320px);
  }
}

/* ============================================================
   CUSTOMER TESTIMONIALS
   3-column card grid on desktop, single column on mobile.
   ============================================================ */
.testimonials {
  position: relative;
  padding: clamp(80px, 14vh, 140px) clamp(32px, 8vw, 120px);
  background: #0a0a0a;
  color: #ffffff;
  /* SWG-27 v7 (Imam May 12): testimonials no longer needs its
     own opacity/transform animation. The scene-6-to-testimonials
     slide motion is now driven by --shell-exit on the
     viewport-shell (mirroring the existing mobile mechanism on
     desktop). As user scrolls into testimonials, scene 6 slides
     off the top via shell-exit, exposing testimonials underneath.
     Text stagger inside testimonials still plays via is-entering. */
  /* Match .trigger sizing (line 1066) — exact 100svh, not min-height,
     so the snap target is unambiguous. Padding stays inside this fixed
     height; if cards ever exceed it the inner content scrolls but the
     snap target boundary stays clean. */
  height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  scroll-snap-align: start;
  /* R4 B9 fix v2 (Adeel Apr 24): was `normal` (Round 3 attempted to
     soften a perceived "jumpy" handoff). The actual cause of the
     jumpiness was inconsistency — every .trigger uses
     `scroll-snap-stop: always` and trackpad momentum was overshooting
     testimonials when its snap was loose. Restoring `always` so
     testimonials hard-snaps the same way scenes do, on desktop. */
  scroll-snap-stop: always;
  /* z-index > .viewport-shell (which is sticky at z:1) so the
     testimonials section actually paints over the scene stage
     when scrolled into view. */
  z-index: 2;
  /* SWG-27 (Adeel May 6): `will-change: transform` removed.
     It was promoting testimonials to its own GPU compositor
     layer on the assumption that helped iOS Safari smoothness.
     On desktop Chrome it actually made the section snap-scroll
     with slightly different physics than the scenes (which
     are NOT GPU-promoted). That mismatch is what Adeel was
     feeling as "not smooth" relative to the rest of the page.
     If iOS regresses we can re-add it inside a mobile
     @media block, but mobile uses horizontal scroll-snap so
     the original rationale likely no longer applies. */
}

.testimonials-header {
  display: grid;
  gap: 14px;
  justify-items: center;
  text-align: center;
  margin-bottom: clamp(48px, 7vh, 80px);
}

.testimonials-eyebrow {
  font-family: "arpona", serif;
  font-size: clamp(0.74rem, 0.9vw, 0.84rem);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: #e8000d;
}

.testimonials-title {
  font-family: "ethnocentric", sans-serif;
  font-size: clamp(2.1rem, 3.4vw, 3rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: 0.01em;
  margin: 0;
  max-width: 22ch;
}

.testimonials-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 2.6vw, 32px);
  max-width: 1200px;
  margin: 0 auto;
}

.testimonial-card {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: clamp(26px, 3vw, 36px);
  background: #111111;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 4px;
}

.testimonial-stars {
  display: flex;
  gap: 2px;
  color: #e8000d;
  font-size: 1.1rem;
  letter-spacing: 1px;
}

.testimonial-body {
  font-family: "arpona", serif;
  font-size: clamp(0.95rem, 1.05vw, 1.02rem);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.92);
  margin: 0;
}

.testimonial-author {
  display: grid;
  gap: 4px;
  margin-top: auto;
}

.testimonial-name {
  font-family: "arpona", serif;
  font-size: 0.98rem;
  font-weight: 600;
  color: #ffffff;
}

.testimonial-role {
  font-family: "arpona", serif;
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(232, 0, 13, 0.82);
}

@media (max-width: 880px) {
  /* Compress hard: all 3 cards + header must fit within 100svh so the
     section plays as a single scroll-snap slide on phones (Adeel
     feedback, Apr 20).
     Top padding: ~80px to clear the fixed Login button (~44px tall +
     22px top inset = ~66px, so 80px leaves a 14px gap).
     Bottom padding: ~56px to leave room for the progress dots. */
  .testimonials {
    padding: 80px clamp(18px, 5vw, 32px) 56px;
    min-height: 100svh;
    justify-content: space-between; /* header top, grid filling rest */
  }
  .testimonials-header {
    margin-bottom: clamp(18px, 2.4vh, 28px);
  }
  .testimonials-title {
    font-size: clamp(1.35rem, 5.4vw, 1.8rem);
    line-height: 1.06;
  }
  .testimonials-eyebrow {
    font-size: clamp(0.75rem, 2.8vw, 0.82rem);
  }
  .testimonials-grid {
    grid-template-columns: 1fr;
    gap: clamp(8px, 1.4vh, 12px);
    flex: 1 1 auto;
    /* space-between distributes cards: first at top, middle in center,
       last at bottom. Previously 'center' left the last card floating
       mid-screen. */
    align-content: space-between;
  }
  .testimonial-card {
    gap: 8px;
    padding: clamp(12px, 2.4vh, 18px) clamp(14px, 4.2vw, 20px);
  }
  .testimonial-stars {
    font-size: 0.9rem;
    letter-spacing: 0.5px;
  }
  .testimonial-body {
    font-size: clamp(0.82rem, 3.4vw, 0.9rem);
    line-height: 1.4;
  }
  /* Name + "VERIFIED BUYER" on the same line on mobile so each card
     is ~1 row shorter, leaving more room for the Login button at top
     and progress dots at bottom. */
  .testimonial-author {
    display: flex;
    flex-direction: row;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
  }
  .testimonial-name {
    font-size: 0.84rem;
  }
  .testimonial-role {
    font-size: 0.75rem;
    letter-spacing: 0.12em;
  }
  .testimonial-role::before {
    content: "· ";
    color: rgba(255, 255, 255, 0.35);
    margin-right: 2px;
  }
}

.scene-composition {
  position: absolute;
  z-index: 4;
  display: grid;
  gap: clamp(16px, 2vw, 24px);
  align-items: end;
  max-width: min(84vw, 980px);
  opacity: 1;
  transition: opacity 220ms ease;
  /* Let taps / hover pass through to the fluid-smoke iframe below.
     Previously this div (z:4) was eating events before they could
     reach the iframe (z:3), so tapping WEATHER PROOF produced no
     smoke even though copy-overlay itself was already pointer-events:
     none. Fix: set the whole composition non-interactive. */
  pointer-events: none;
}

.scene-composition.is-hidden {
  opacity: 0;
}

/* Staggered text entrance — fires on every scene change. `both` fill
   mode keeps the final translate/opacity state; no reverse on exit. */
@keyframes scene-text-enter {
  0%   { opacity: 0; transform: translateY(28px); }
  100% { opacity: 1; transform: translateY(0); }
}
.scene-composition.is-entering .scene-title-line,
.scene-composition.is-entering .scene-subline,
.scene-composition.is-entering .badge-pill {
  animation: scene-text-enter 700ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.scene-composition.is-entering .scene-title-line:nth-child(1) { animation-delay: 60ms; }
.scene-composition.is-entering .scene-title-line:nth-child(2) { animation-delay: 180ms; }
.scene-composition.is-entering .scene-subline              { animation-delay: 320ms; }
.scene-composition.is-entering .badge-pill:nth-child(1)     { animation-delay: 440ms; }
.scene-composition.is-entering .badge-pill:nth-child(2)     { animation-delay: 520ms; }
.scene-composition.is-entering .badge-pill:nth-child(3)     { animation-delay: 600ms; }
.scene-composition.is-entering .badge-pill:nth-child(4)     { animation-delay: 680ms; }

/* Respect reduced-motion users */
@media (prefers-reduced-motion: reduce) {
  .scene-composition.is-entering .scene-title-line,
  .scene-composition.is-entering .scene-subline,
  .scene-composition.is-entering .badge-pill {
    animation: none;
  }
}

.scene-composition.layout-copy-demo {
  grid-template-columns: minmax(300px, var(--copy-width, 38vw)) minmax(170px, var(--demo-width, 22vw));
  justify-content: space-between;
}

.scene-composition.layout-copy-demo.pos-left-center,
.scene-composition.layout-copy-demo.pos-left-lower,
.scene-composition.layout-copy-demo.pos-left-upper {
  right: clamp(28px, 6vw, 92px);
  max-width: none;
}

.scene-composition.layout-demo-copy {
  grid-template-columns: minmax(170px, var(--demo-width, 22vw)) minmax(300px, var(--copy-width, 38vw));
}

.scene-composition.layout-copy-stack {
  grid-template-columns: minmax(300px, var(--copy-width, 38vw));
  gap: 18px;
  justify-items: start;
}

/* Right-positioned copy-stack: align the grid track + items to the right edge */
.scene-composition.layout-copy-stack.pos-right-center,
.scene-composition.layout-copy-stack.pos-right-lower,
.scene-composition.layout-copy-stack.pos-right-upper {
  justify-content: end;
  justify-items: end;
}

.scene-composition.pos-right-center {
  top: 50%;
  right: var(--scene-right, clamp(112px, 11vw, 220px));
  transform: translateY(-50%);
}

.scene-composition.pos-right-lower {
  right: var(--scene-right, clamp(112px, 11vw, 220px));
  bottom: clamp(64px, 9vh, 104px);
}

.scene-composition.pos-left-center {
  top: 50%;
  left: clamp(28px, 6vw, 92px);
  transform: translateY(-50%);
}

.scene-composition.pos-left-lower {
  left: clamp(28px, 6vw, 92px);
  bottom: clamp(64px, 9vh, 104px);
}

.scene-composition.pos-left-upper {
  top: clamp(118px, 17vh, 180px);
  left: clamp(28px, 6vw, 92px);
}

.copy-overlay {
  width: min(var(--copy-width, 38vw), 640px);
  /* Let cursor/touch events pass through to the smoke iframe below.
     Previously 'pointer-events: auto' for text selection blocked
     smoke reactions over the UNSTOPPABLE/WEATHER PROOF/etc text
     areas. For a landing page prototype, having smoke follow the
     cursor/finger across the whole viewport is more valuable than
     selectable text. */
  pointer-events: none;
  transform: translateY(var(--copy-shift-y, 0));
}

.copy-inner {
  display: grid;
  gap: 18px;
  justify-items: var(--copy-justify, start);
  text-align: var(--copy-align, left);
  filter: drop-shadow(0 10px 28px var(--shadow));
}

/* Right-positioned compositions: anchor copy + badges to the right edge */
.scene-composition.pos-right-center .copy-inner,
.scene-composition.pos-right-lower .copy-inner,
.scene-composition.pos-right-upper .copy-inner {
  justify-items: end;
  text-align: right;
}

.scene-composition.pos-right-center .badge-row,
.scene-composition.pos-right-lower .badge-row,
.scene-composition.pos-right-upper .badge-row {
  justify-content: flex-end;
}

.copy-accent {
  width: 92px;
  height: 3px;
  background: var(--brand-red);
  box-shadow: 0 0 20px rgba(232, 0, 13, 0.2);
}

/* SWG-69 follow-up: hide the bottom-left scene-composition entirely
   when the centered hero overlay owns scene 1. Toggled in setScene()
   by checking content.titleHtml. */
.scene-composition.is-hero-empty {
  display: none !important;
}

.scene-title {
  margin: 0;
  max-width: var(--title-max, 12ch);
  font-family: "ethnocentric", sans-serif;
  font-size: clamp(2.2rem, var(--title-fluid, 4.2vw), var(--title-max-size, 4.6rem));
  line-height: 0.9;
  letter-spacing: 0.035em;
  text-transform: uppercase;
  color: var(--brand-grey);
  text-shadow:
    0 2px 0 rgba(0, 0, 0, 0.22),
    0 14px 36px rgba(0, 0, 0, 0.62);
  white-space: normal;
}

.scene-title-line {
  display: block;
}

.scene-title-accent {
  color: var(--brand-red);
}

.scene-subline {
  margin: 0;
  max-width: var(--subline-max, 26ch);
  color: rgba(255, 255, 255, 0.94);
  font-family: "arpona", serif;
  font-size: clamp(1.06rem, 1.35vw, 1.34rem);
  font-weight: 500;
  line-height: 1.48;
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 0.16),
    0 8px 22px rgba(0, 0, 0, 0.52);
}

.badge-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.badge-row:empty {
  display: none;
}

.badge-pill {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 15px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  color: rgba(255, 255, 255, 0.96);
  font-family: "arpona", serif;
  font-size: 0.98rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  backdrop-filter: blur(10px);
}

.progress {
  position: fixed;
  top: 50%;
  right: clamp(12px, 2vw, 26px);
  z-index: 5;
  display: grid;
  gap: 15px;
  transform: translateY(-50%);
  /* Progress dots are visual indicators only — let smoke through */
  pointer-events: none;
}

.progress-dot {
  position: relative;
  width: 24px;
  height: 30px;
  transition:
    opacity 220ms ease,
    box-shadow 220ms ease,
    transform 220ms ease;
}

.progress-dot::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 11px;
  height: 11px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  transition:
    opacity 220ms ease,
    transform 220ms ease,
    box-shadow 220ms ease,
    background-color 220ms ease;
}

.progress-dot::after {
  content: "";
  position: absolute;
  inset: 0;
  clip-path: path("M12 1C16.4 1 19.7 2.3 22 4.2C22.6 8.7 21.9 14.1 19.8 18.3C17.8 22.3 15.1 25.4 12 27.5C8.9 25.4 6.2 22.3 4.2 18.3C2.1 14.1 1.4 8.7 2 4.2C4.3 2.3 7.6 1 12 1Z");
  background: var(--brand-red);
  opacity: 0;
  transform: scale(0.84);
  transition:
    opacity 220ms ease,
    transform 220ms ease,
    box-shadow 220ms ease,
    background-color 220ms ease;
}

.progress-dot.is-active {
  transform: scale(1.08);
}

.progress-dot.is-active::before {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.4);
}

.progress-dot.is-active::after {
  opacity: 1;
  transform: scale(1);
  box-shadow:
    0 0 0 8px rgba(232, 0, 13, 0.08),
    0 0 18px rgba(232, 0, 13, 0.18);
}

.triggers {
  position: relative;
  margin-top: -100svh;
}

.trigger {
  height: 100svh;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

.trigger:first-child,
.trigger:last-child {
  height: 100svh;
}

/* ============================================================
   Mobile horizontal scroll (R4 B3 — Adeel Apr 23).
   Desktop/tablet architecture above is untouched. Below 880px
   we flip the scroll axis so users swipe left/right between
   scenes instead of scrolling up/down. All existing UI stays
   identical — same progress dots, same Login button, same
   smoke overlay, same sticky viewport-shell, same triggers,
   same IntersectionObserver. Only the layout direction changes.
   ============================================================ */
@media (max-width: 880px) {
  html {
    scroll-snap-type: none;
    height: 100%;
    overflow: hidden;
  }
  body {
    height: 100%;
    min-height: 0;
    overflow-x: hidden;
    overflow-y: hidden;
    display: block;
    width: 100%;
  }
  main.vslocks-home-scroll {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    width: 100vw;
    max-width: 100vw;
    height: 100svh;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  main.vslocks-home-scroll::-webkit-scrollbar { display: none; }
  .viewport-shell {
    position: sticky;
    top: 0;
    left: 0;
    flex: 0 0 100vw;
    width: 100vw;
    z-index: 1;
    /* When user scrolls from scene 6 into testimonials (last 100vw of
       scroll range), translate viewport-shell left so scene 6 slides
       OUT while testimonials slides IN. Without this, scene 6 stays
       pinned and testimonials looks like it's "closing a drawer"
       over it. The scroll listener writes --shell-exit ∈ [0, 1] as
       scroll-x crosses the scene-6 → testimonials boundary. */
    transform: translate3d(calc(var(--shell-exit, 0) * -100vw), 0, 0);
  }
  /* media-stack already has translate3d(0, var(--scene-offset) * -100svh, 0)
     for desktop vertical slide. On mobile we rotate it 90deg — flex row +
     horizontal translate. The JS scroll listener writes scroll-progress
     into --scene-offset (fractional) so the media slides in real time
     with the finger, not just on snap. */
  .media-stack {
    flex-direction: row;
    width: 700vw; /* 6 scenes + testimonials stacked horizontally (SWG-27 v8) */
    transform: translate3d(
      calc(var(--scene-offset, 0) * -100vw),
      0,
      0
    );
    transition: none; /* scroll listener drives it; no catch-up anim */
  }
  .stage-layer {
    flex: 0 0 100vw;
    width: 100vw;
    height: 100svh;
  }
  .triggers {
    display: flex;
    flex-direction: row;
    flex: 0 0 auto;
    margin-top: 0;
    margin-left: -100vw; /* overlap the sticky viewport-shell */
  }
  .trigger {
    flex: 0 0 100vw;
    width: 100vw;
    height: 100svh;
  }
  .testimonials {
    flex: 0 0 100vw;
    width: 100vw;
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }
}

/* Mobile smoke overlay touch split:
   On real touch devices, `touch-action: pan-x` tells the browser
   "horizontal drags belong to the scroll container (body), other
   gestures (tap, vertical drag) belong to the element." So the
   smoke iframe can keep pointer-events: auto to react to taps
   while horizontal swipes still scroll the page. This only works
   for touch input — mouse drags in DevTools without touch
   emulation will be eaten by the overlay, which is why we also
   added the mouse-drag-to-scroll JS shim below. */
@media (max-width: 880px) {
  .fluid-smoke-overlay.is-active,
  .fluid-smoke-overlay.is-active iframe {
    touch-action: pan-x;
  }
}

/* No-touch fallback: DevTools inspection on desktop without touch
   emulation only fires mouse events, and the smoke iframe would
   eat mouse drags before our shim can scroll. On those environments
   we disable pointer capture on the smoke so the shim wins.
   Real touch devices keep pointer-events: auto (from above) and
   get both behaviors: tap drives smoke, horizontal swipe scrolls. */
@media (max-width: 880px) {
  html.no-touch .fluid-smoke-overlay,
  html.no-touch .fluid-smoke-overlay.is-active,
  html.no-touch .fluid-smoke-overlay iframe {
    pointer-events: none !important;
  }
}

@media (max-width: 1080px) {
  .hero-logo {
    width: min(32vw, 230px);
  }

  .scene-composition.layout-copy-demo,
  .scene-composition.layout-demo-copy {
    grid-template-columns: minmax(260px, var(--copy-width, 38vw)) minmax(150px, var(--demo-width, 22vw));
  }
}

@media (max-width: 980px) {
  .viewport-shell {
    height: 100dvh;
  }

  .stage::before {
    background:
      linear-gradient(180deg, rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0.46) 56%, rgba(0, 0, 0, 0.82)),
      linear-gradient(180deg, rgba(0, 0, 0, 0.38), transparent 16%, transparent 42%, rgba(0, 0, 0, 0.72) 100%);
  }

  .hero-logo {
    /* SWG-72 (Adeel May 6) + SWG-90: Center the logo on mobile but nudge
       it slightly right to optically balance the left search+cart cluster
       vs the right hamburger. Pure 50% centering now feels pulled left
       because of the two icons on the left side. */
    top: clamp(14px, 3svh, 22px);
    left: 50%;
    height: 44px;
    width: auto;
    max-width: calc(100vw - 168px);
    /* Combine horizontal centering + small right nudge with the slide-down entrance */
    transform: translate3d(calc(-50% + 10px), -10px, 0);
  }
  .hero-logo img {
    height: 100%;
    width: auto;
  }

  .hero-logo.is-visible {
    transform: translate3d(calc(-50% + 10px), 0, 0);
  }

  /* SWG-73 mobile decision (Imam May 11) — REVERSED by Adeel R5 feedback 2026-05-21:
     Icons are now visible on mobile, positioned LEFT of the centered logo
     (search + cart cluster top-left; hamburger top-right). This matches
     the desktop quick-access pattern Adeel referenced. Logo remains centered.
     44×44 tap targets. No overlap at 320px+ widths. */
  .mobile-utility-icon {
    display: inline-flex !important;
  }
  .utility-bar {
    display: none !important; /* still hidden on mobile per earlier decision; login stays in panel */
  }

  /* SWG-88: left-side cluster for search + cart on mobile (replaces the previous right-side
     individual positioning that only applied on desktop). */
  @media (max-width: 767.98px) {
    .mobile-utility-cluster {
      position: fixed;
      top: clamp(14px, 3svh, 22px);
      left: clamp(14px, 4vw, 22px);
      display: flex;
      gap: 6px;
      z-index: 21;
    }
    .mobile-utility-icon-search,
    .mobile-utility-icon-cart {
      position: static;
      width: 44px;
      height: 44px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
    }
  }

  .utility-bar {
    top: 14px;
    right: 14px;
    gap: 8px;
  }

  .utility-link {
    min-width: 76px;
    min-height: 36px;
    padding: 0 12px;
    font-size: 0.88rem;
  }

  .scene-composition,
  .scene-composition.pos-right-center,
  .scene-composition.pos-right-lower,
  .scene-composition.pos-left-center,
  .scene-composition.pos-left-lower,
  .scene-composition.pos-left-upper {
    left: 18px;
    right: 18px;
    bottom: 88px;
    top: auto;
    transform: none;
    grid-template-columns: 1fr;
    gap: 10px;
  }

  /* Mobile reset: pos-right scenes lose their right-alignment because
     the composition is now full-width, and right-anchored text would
     overflow off the screen. Fall back to default left alignment. */
  .scene-composition.layout-copy-stack.pos-right-center,
  .scene-composition.layout-copy-stack.pos-right-lower,
  .scene-composition.layout-copy-stack.pos-right-upper {
    justify-content: start;
    justify-items: start;
  }

  .scene-composition.pos-right-center .copy-inner,
  .scene-composition.pos-right-lower .copy-inner,
  .scene-composition.pos-right-upper .copy-inner {
    justify-items: start;
    text-align: left;
  }

  .scene-composition.pos-right-center .badge-row,
  .scene-composition.pos-right-lower .badge-row,
  .scene-composition.pos-right-upper .badge-row {
    justify-content: flex-start;
  }

  .scene-composition.layout-demo-copy .copy-overlay {
    order: 1;
  }

  .scene-composition.layout-copy-stack {
    gap: 10px;
  }

  .scene-title {
    max-width: var(--title-max-mobile, 11ch);
    font-size: clamp(1.42rem, 6.8vw, 2.16rem);
    line-height: 0.9;
  }

  .scene-subline {
    max-width: 20ch;
    font-size: 0.9rem;
  }

  .copy-overlay {
    transform: translateY(var(--copy-shift-y-mobile, var(--copy-shift-y, 0)));
  }

  .progress {
    right: 50%;
    top: auto;
    bottom: 12px;
    transform: translateX(50%);
    grid-auto-flow: column;
    gap: 10px;
  }

}

@media (max-width: 720px) {
  .stage-layer[data-scene-layer="1"] {
    /* SWG-70 (Adeel May 13): mobile now loads a native 9:16 portrait
       clip (hero-mobile-test.mp4 via the <source media> swap), so
       `cover` fills the phone viewport edge-to-edge with no letterbox
       and no crop. This supersedes the SWG-68 `contain` stopgap,
       which was only needed while mobile was forced to use the 16:9
       desktop source. Desktop / ultrawide unchanged. */
    object-fit: cover;
    object-position: center center;
  }

  .stage-layer[data-scene-layer="4"] {
    object-position: center center;
  }

  .stage-layer[data-scene-layer="5"] {
    object-position: 44% center;
  }

  .stage-layer[data-scene-layer="6"] {
    object-position: 72% center;
  }

  .badge-pill {
    min-height: 34px;
    font-size: 0.82rem;
  }
}

