/* Chair configurator overlay. Lives on top of the existing hero still until
   the Meshopt GLB is ready, then the still fades and the transparent canvas
   keeps the clouds. */

.hero-config-frame {
  z-index: 3;
  opacity: 0;
  pointer-events: none;
}
.hero-config-canvas {
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  background: transparent;
  image-rendering: auto;
  touch-action: none;
  transition: opacity 420ms ease;
}
.hero-config-spinner {
  position: absolute;
  z-index: 9;
  left: 50%;
  top: 50%;
  display: none;
  place-items: center;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.hero-load {
  display: grid;
  justify-items: center;
  gap: 8px;
  pointer-events: none;
}
.hero-load-bar,
.hero-config-bar {
  position: relative;
  width: 92px;
  height: 9px;
  overflow: hidden;
  box-sizing: border-box;
  border: 1.5px solid #1b3568;
  border-radius: 99px;
  background: transparent;
  box-shadow: none;
}
.hero-load-bar::after,
.hero-config-bar::after {
  position: absolute;
  top: 1px;
  bottom: 1px;
  left: 1px;
  width: 0;
  border-radius: inherit;
  background: #8cb4ff;
  box-shadow: none;
  content: "";
  animation: hero-load-fill 1300ms ease-in-out infinite;
}
.hero-load-label {
  color: #1b3568;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: .56rem;
  font-weight: 500;
  letter-spacing: .14em;
  line-height: 1;
  text-transform: uppercase;
}

.hero-chair-ui {
  position: absolute;
  z-index: 8;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 240ms ease;
}
.hero-scene[data-mode="configurator"] {
  --chair-stage-w: min(46vw, 620px);
  --chair-ui-inset: clamp(44px, 4.2vw, 64px);
}
.hero-chair-col {
  position: absolute;
  top: 36%;
  display: grid;
  justify-items: center;
  gap: 0;
  pointer-events: none;
}
.hero-chair-col--left {
  left: calc(50% - (var(--chair-stage-w) / 2) + var(--chair-ui-inset));
  transform: translate(-50%, -10%);
}
.hero-chair-col--right {
  left: calc(50% + (var(--chair-stage-w) / 2) - var(--chair-ui-inset));
  transform: translate(-50%, -10%);
}
.hero-chair-group {
  display: grid;
  justify-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
}
.hero-chair-group--lower {
  margin-top: 26px;
}
.hero-chair-label {
  margin: 0;
  color: var(--muted);
  font-family: var(--mono);
  font-size: .56rem;
  font-weight: 500;
  letter-spacing: .14em;
  line-height: 1;
  text-transform: uppercase;
}
.hero-chair-swatches,
.hero-chair-pills,
.hero-chair-bases {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
}
.hero-chair-swatches { flex-direction: column; }
.hero-chair-swatch,
.hero-chair-base {
  position: relative;
}
.hero-chair-swatch {
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent);
  border-radius: 50%;
  background: var(--swatch);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--surface) 50%, transparent);
  cursor: pointer;
  pointer-events: auto;
  touch-action: manipulation;
}
.hero-chair-swatch[aria-pressed="true"] {
  box-shadow: 0 0 0 2px var(--paper), 0 0 0 3px var(--ink);
}
.hero-chair-tip {
  position: absolute;
  top: 50%;
  z-index: 3;
  padding: 0;
  color: var(--ink);
  font-family: var(--mono);
  font-size: .52rem;
  letter-spacing: .04em;
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease, transform 140ms ease;
}
.hero-chair-col--left .hero-chair-swatch .hero-chair-tip {
  left: calc(100% + 10px);
  right: auto;
  transform: translateY(-50%) translateX(-4px);
}
.hero-chair-col--right .hero-chair-swatch .hero-chair-tip {
  right: calc(100% + 10px);
  left: auto;
  transform: translateY(-50%) translateX(4px);
}
.hero-chair-base .hero-chair-tip {
  left: 50%;
  right: auto;
  top: calc(100% + 8px);
  bottom: auto;
  transform: translateX(-50%) translateY(-4px);
}
.hero-chair-col--left .hero-chair-swatch:hover .hero-chair-tip,
.hero-chair-col--left .hero-chair-swatch:focus-visible .hero-chair-tip {
  opacity: 1;
  transform: translateY(-50%);
}
.hero-chair-col--right .hero-chair-swatch:hover .hero-chair-tip,
.hero-chair-col--right .hero-chair-swatch:focus-visible .hero-chair-tip {
  opacity: 1;
  transform: translateY(-50%);
}
.hero-chair-base:hover .hero-chair-tip,
.hero-chair-base:focus-visible .hero-chair-tip {
  opacity: 1;
  transform: translateX(-50%);
}
.hero-chair-pill {
  min-height: 30px;
  padding: 0 12px;
  border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  color: var(--ink);
  font-family: var(--mono);
  font-size: .56rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
  pointer-events: auto;
  touch-action: manipulation;
}
.hero-chair-pill[aria-pressed="true"] {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
}
.hero-chair-base {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent);
  border-radius: 50%;
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  color: var(--ink);
  cursor: pointer;
  pointer-events: auto;
  touch-action: manipulation;
}
.hero-chair-base svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-width: 1.4;
}
.hero-chair-base[aria-pressed="true"] {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
}

html .hero-media-visual,
html .hero-still-stack,
html .hero-video-frame,
html .hero-video-frame video,
html .hero-model-frame,
html .hero-product-image {
  -webkit-mask-image: none !important;
  mask-image: none !important;
}
html .hero-scene[data-mode="imagery"] .hero-product-ghost,
html .hero-scene[data-mode="imagery"] .hero-product-stage::after {
  display: none !important;
}
html .hero-scene[data-mode="imagery"] .hero-media-visual,
html .hero-scene[data-mode="imagery"] .hero-still-stack,
html .hero-scene[data-mode="imagery"] .hero-product-stage {
  background: transparent !important;
  -webkit-mask-image: none !important;
  mask-image: none !important;
}
html .hero-scene[data-mode="imagery"] .hero-still-stack {
  position: absolute;
  inset: 0 !important;
  width: auto !important;
  height: auto !important;
  display: grid;
  place-items: center;
  overflow: visible;
  transform: none;
}
html .hero-scene[data-mode="imagery"] .hero-product-image {
  position: relative;
  inset: auto !important;
  display: block;
  object-fit: cover;
  filter: none !important;
  transform: none !important;
  -webkit-mask-image: none !important;
  mask-image: none !important;
}
.hero-scene[data-mode="configurator"] .hero-config-frame,
.hero-scene[data-mode="configurator"] .hero-media-layer {
  -webkit-mask-image: none !important;
  mask-image: none !important;
}
.hero-scene[data-mode="configurator"] .hero-haze {
  opacity: 0 !important;
}
html .hero-scene[data-mode="configurator"] .hero-config-frame {
  opacity: 1 !important;
  transition: none !important;
}
html .hero-scene[data-mode="configurator"]:not(.is-chair-ready) .hero-config-canvas {
  opacity: 0 !important;
}
html .hero-scene[data-mode="configurator"].is-chair-ready .hero-config-canvas {
  opacity: 1 !important;
  pointer-events: auto;
  transition: none !important;
}
.hero-scene[data-mode="configurator"]:not(.is-chair-ready):not(.is-chair-failed) .hero-config-spinner {
  display: grid;
}
html .hero-scene[data-mode="configurator"] .hero-media-layer:not(.hero-config-frame),
html .hero-scene[data-mode="configurator"].is-ready .hero-media-layer:not(.hero-config-frame),
html .hero-scene[data-mode="configurator"].is-ready:not(.is-idle) .hero-still-stack,
html .hero-scene[data-mode="configurator"] .hero-still-stack,
html .hero-scene[data-mode="configurator"] .hero-product-image,
html .hero-scene[data-mode="configurator"] .hero-product-ghost,
html .hero-scene[data-mode="configurator"] .hero-video-frame,
html .hero-scene[data-mode="configurator"] .hero-play {
  display: none !important;
  opacity: 0 !important;
  visibility: hidden !important;
  filter: none !important;
  pointer-events: none !important;
}
html .hero-scene[data-mode="configurator"] .hero-model-frame,
html .hero-scene[data-mode="configurator"].is-model-ready .hero-model-frame {
  opacity: 0 !important;
  pointer-events: none !important;
}
html .hero-scene[data-mode="configurator"] .hero-configurator-controls,
html .hero-scene[data-mode="configurator"].is-model-ready:not(.is-roulette) .hero-configurator-controls {
  display: none !important;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero-scene[data-mode="configurator"]:not(.is-roulette):not(.is-preparing).is-chair-ready .hero-chair-ui {
  opacity: 1 !important;
  visibility: visible;
}
.hero-scene.is-media-locked .hero-chair-ui,
.hero-scene.is-roulette .hero-chair-ui,
.hero-scene.is-preparing .hero-chair-ui,
.hero-scene.is-source-visible .hero-chair-ui {
  opacity: 0 !important;
  visibility: hidden;
  pointer-events: none !important;
}
.hero-scene.is-roulette .hero-config-frame,
.hero-scene.is-preparing .hero-config-frame {
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero-scene.is-chair-dragging { cursor: grabbing; }

html[data-skin="nocturne"] .hero-chair-swatch[aria-pressed="true"] {
  box-shadow: 0 0 0 2px #0c1220, 0 0 0 3.5px #f1f5f9;
}
html[data-skin="nocturne"] .hero-load-bar,
html[data-skin="nocturne"] .hero-config-bar {
  border-color: #8cb4ff;
  background: transparent;
}
html[data-skin="nocturne"] .hero-load-bar::after,
html[data-skin="nocturne"] .hero-config-bar::after {
  background: #9ec4ff;
  box-shadow: none;
}
html[data-skin="nocturne"] .hero-load-label {
  color: #c5d8ff;
}

html .hero-scene[data-mode="3d"] .hero-still-stack,
html .hero-scene[data-mode="3d"] .hero-product-image,
html .hero-scene[data-mode="3d"] .hero-product-ghost,
html .hero-scene[data-mode="3d"] .hero-play,
html .hero-scene[data-mode="3d"] .hero-configurator-controls,
html .hero-scene[data-mode="3d"] .hero-viewfinder,
html .hero-scene[data-mode="3d"] .hero-config-spinner,
html .hero-scene[data-mode="ar"] .hero-still-stack,
html .hero-scene[data-mode="ar"] .hero-product-image,
html .hero-scene[data-mode="ar"] .hero-product-ghost,
html .hero-scene[data-mode="ar"] .hero-video-frame,
html .hero-scene[data-mode="ar"] .hero-play,
html .hero-scene[data-mode="ar"] .hero-configurator-controls,
html .hero-scene[data-mode="ar"] .hero-viewfinder,
html .hero-scene[data-mode="ar"] .hero-config-spinner {
  display: none !important;
  opacity: 0 !important;
  visibility: hidden !important;
  filter: none !important;
  pointer-events: none !important;
}
html .hero-scene[data-mode="video"] .hero-video-frame,
html .hero-scene[data-mode="3d"] .hero-video-frame,
html .hero-scene[data-mode="ugc"] .hero-video-frame {
  inset: auto !important;
  left: 50% !important;
  top: 50% !important;
  right: auto !important;
  bottom: auto !important;
  height: auto !important;
  max-height: none !important;
  min-height: 0 !important;
  transform: translate(-50%, -50%);
  overflow: hidden;
  border-radius: 16px;
  background: none !important;
}
html .hero-scene[data-mode="video"] .hero-video-frame::after,
html .hero-scene[data-mode="3d"] .hero-video-frame::after,
html .hero-scene[data-mode="ugc"] .hero-video-frame::after,
html .hero-scene[data-mode="video"] .hero-video-frame::before,
html .hero-scene[data-mode="3d"] .hero-video-frame::before,
html .hero-scene[data-mode="ugc"] .hero-video-frame::before {
  content: none !important;
  display: none !important;
}
html .hero-scene[data-mode="video"] .hero-video-frame video,
html .hero-scene[data-mode="3d"] .hero-video-frame video,
html .hero-scene[data-mode="ugc"] .hero-video-frame video,
html .hero-scene[data-mode="imagery"] .hero-product-image {
  position: relative !important;
  inset: auto !important;
  width: 100% !important;
  height: 100% !important;
  max-height: none !important;
  min-height: 0 !important;
  object-fit: cover !important;
  object-position: center !important;
  border-radius: 16px !important;
  background: none !important;
  background-image: none !important;
}
html .hero-scene[data-mode="imagery"] .hero-product-image {
  width: min(70%, 600px) !important;
  height: auto !important;
}
@media (min-width: 861px) {
  /* Visible plate is 5:3 — wider than a straight 4:3, which stood too tall
     against the hero and pushed the plate into the lede and the button below it. */
  html .hero-scene[data-mode="video"] .hero-video-frame,
  html .hero-scene[data-mode="3d"] .hero-video-frame {
    width: 100% !important;
    height: auto !important;
    max-height: none !important;
    aspect-ratio: 5 / 3;
  }
  html .hero-scene[data-mode="video"] .hero-video-frame video,
  html .hero-scene[data-mode="3d"] .hero-video-frame video,
  html .hero-scene[data-mode="imagery"] .hero-product-image {
    width: 100% !important;
    height: auto !important;
    max-height: none !important;
    aspect-ratio: 5 / 3;
  }
  /* UGC is the social crop: a portrait card, and a smaller one than the
     landscape plate. The stage carries the shape rather than the frame inside
     it, because the pause control is positioned against the stage — with only
     the frame shaped, the stage stayed a wide empty box and the button sat out
     in the middle of nothing, well to the left of the video. */
  html .hero.has-sky .hero-scene[data-mode="ugc"] .hero-product-stage,
  html .hero.has-sky-fallback .hero-scene[data-mode="ugc"] .hero-product-stage {
    width: min(23vw, 296px) !important;
    height: auto !important;
    max-height: none !important;
    aspect-ratio: 3 / 4;
    overflow: hidden;
    border-radius: 16px;
  }
  html .hero.has-sky .hero-scene[data-mode="ugc"] .hero-media-visual,
  html .hero.has-sky-fallback .hero-scene[data-mode="ugc"] .hero-media-visual {
    inset: 0 !important;
    width: auto !important;
    height: auto !important;
    transform: none !important;
  }
  html .hero-scene[data-mode="ugc"] .hero-video-frame {
    left: 0 !important;
    top: 0 !important;
    width: 100% !important;
    height: 100% !important;
    aspect-ratio: auto !important;
    transform: none !important;
  }
  html .hero-scene[data-mode="3d"] .hero-video-frame,
  html .hero-scene[data-mode="3d"] .hero-play,
  html .hero-scene[data-mode="video"] .hero-video-frame,
  html .hero-scene[data-mode="ugc"] .hero-video-frame {
    display: grid !important;
    opacity: 1 !important;
    visibility: visible !important;
    pointer-events: auto !important;
    filter: none !important;
  }
  html .hero-scene[data-mode="3d"] .hero-ar-shell,
  html .hero-scene[data-mode="3d"] .hero-model-frame,
  html .hero-scene[data-mode="3d"] .hero-ar-device {
    display: none !important;
    visibility: hidden !important;
    pointer-events: none !important;
  }
}
@media (max-width: 860px) {
  /* ── One plate, every mode ────────────────────────────────────────────────
     The stage is the box that sits in the flow above the mode list, so a mode
     that wanted a taller stage pushed the list and the call to action down the
     page: the configurator alone moved everything below it by 56px, and AR and
     the stills each had a box of their own again. Switching service therefore
     made the whole hero jump.

     Every mode now shares one 3.33:4 plate at one position and one size —
     films, UGC, 3D, stills, the configurator and AR. Only what is inside it
     changes. The plate's height is the room the hero can spare and its width
     follows from the ratio, so it grows to the space available and never past
     it. Carried on the scene rather than the stage so the chair controls can
     line up with the same edges. */
  html .hero .hero-scene {
    /* The plate was leaving a band of empty page down each side and a gap under
       the title. The height it can take is capped by what is below it — the
       service list and the call to action both have to stay on the first screen
       — so the width cap comes up to just short of that, and the ratio does the
       rest. Measured settled, this leaves the button clear of the fold.

       Raised once the headline came down to one line, and again once "Show
       input" moved out to its own row under the plate.

       The height is no longer a fraction of the viewport. Everything below the
       plate -- the chip row, the service list, the mode readout and the call to
       action -- is a fixed stack about 276px tall, and the copy above it about
       120px, so what the plate can have is the viewport minus that overhead,
       not a percentage of it. A percentage gets this right at one screen height
       and wrong everywhere else: 52svh fits an 852px phone and overflows a
       740px one by 35px. Subtracting the overhead tracks every height, and the
       436px ceiling stops it growing past its own art on a tall screen. Width
       still follows the ratio, capped at 92vw so it keeps a margin either
       side. */
    --hero-plate-h: min(calc(100svh - 400px), 448px);
    --hero-plate-w: min(96vw, calc(var(--hero-plate-h) * 3.33 / 4));
  }
  html .hero .hero-scene .hero-product-stage {
    width: var(--hero-plate-w) !important;
    aspect-ratio: 3.33 / 4 !important;
    /* Nudged up off the flow line. The plate sat with more air above it than
       below; a relative offset moves what you see without moving the mode list
       under it, which is what evens the two gaps out. */
    top: -6px;
    height: auto !important;
    max-height: none !important;
    margin-left: auto;
    margin-right: auto;
    overflow: hidden;
    border-radius: 18px;
  }
  /* Inside, everything simply fills the plate, so the video, the poster, the
     canvas and the rounded corner all agree on one box. The visual layer
     normally bleeds a few pixels past the stage for the drifting-still
     treatment; on a shaped plate that bleed is what the corner would clip. */
  html .hero-scene[data-mode="video"] .hero-media-visual,
  html .hero-scene[data-mode="3d"] .hero-media-visual,
  html .hero-scene[data-mode="ugc"] .hero-media-visual,
  html .hero-scene[data-mode="imagery"] .hero-media-visual,
  html .hero-scene[data-mode="configurator"] .hero-media-visual,
  html .hero-scene[data-mode="ar"] .hero-media-visual {
    inset: 0 !important;
    width: auto !important;
    height: auto !important;
    transform: none !important;
  }
  html .hero-scene[data-mode="video"] .hero-video-frame,
  html .hero-scene[data-mode="3d"] .hero-video-frame,
  html .hero-scene[data-mode="ugc"] .hero-video-frame,
  html .hero-scene[data-mode="imagery"] .hero-still-stack,
  html .hero-scene[data-mode="imagery"] .hero-product-image,
  html .hero-scene[data-mode="configurator"] .hero-config-frame,
  html .hero-scene[data-mode="configurator"] .hero-config-canvas {
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    max-height: none !important;
    inset: 0 !important;
    /* These layers are centred elsewhere with a -50%/-50% translate against a
       left/top of 50%. Filling the plate means pinning to its corners, so the
       translate has to go with the offsets or the layer lands a half-box up
       and to the left of the plate it is meant to fill. */
    transform: none !important;
    aspect-ratio: auto !important;
  }
  /* The AR handset sits inside the same plate rather than sizing itself off the
     viewport, so the device mock lands on the plate's centre like every other
     mode's content does. */
  html .hero .hero-scene[data-mode="3d"] .hero-ar-shell,
  html .hero .hero-scene[data-mode="ar"] .hero-ar-shell,
  html .hero.has-sky .hero-scene[data-mode="3d"] .hero-ar-shell,
  html .hero.has-sky .hero-scene[data-mode="ar"] .hero-ar-shell {
    left: 50% !important;
    top: 50% !important;
    width: auto !important;
    height: 92% !important;
    max-width: 90% !important;
    max-height: none !important;
    aspect-ratio: .72;
    transform: translate(-50%, -50%);
  }
  html .hero-scene[data-mode="video"] .hero-video-frame,
  html .hero-scene[data-mode="3d"] .hero-video-frame,
  html .hero-scene[data-mode="ugc"] .hero-video-frame {
    border-radius: 18px;
  }
  /* The still fills its plate exactly as the films fill theirs — same box, same
     corner, same crop behaviour. It had been letterboxed inside a padded grey
     card, which gave imagery a visibly different height and squared-off corners
     next to every other mode. Filling means the plate is what you see, so the
     source has to be cut for this shape; a landscape original will crop hard. */
  html .hero-scene[data-mode="imagery"] .hero-product-image,
  html .hero-scene[data-mode="imagery"] .hero-product-ghost {
    border-radius: 18px !important;
    object-fit: cover !important;
    object-position: center !important;
    background: none !important;
    padding: 0 !important;
    box-sizing: border-box !important;
  }
  /* hero-imagery-pad */
  html .hero-scene[data-mode="imagery"] .hero-still-stack picture {
    display: contents;
  }
  html .hero-scene[data-mode="3d"] .hero-video-frame,
  html .hero-scene[data-mode="3d"] .hero-play,
  html .hero-scene[data-mode="video"] .hero-video-frame,
  html .hero-scene[data-mode="ugc"] .hero-video-frame {
    display: grid !important;
    opacity: 1 !important;
    visibility: visible !important;
    pointer-events: auto !important;
    filter: none !important;
  }
  html .hero-scene[data-mode="3d"] .hero-ar-shell,
  html .hero-scene[data-mode="3d"] .hero-model-frame,
  html .hero-scene[data-mode="3d"] .hero-ar-device {
    display: none !important;
    visibility: hidden !important;
    pointer-events: none !important;
  }
}
html .hero-scene[data-mode="video"] .hero-still-stack,
html .hero-scene[data-mode="video"] .hero-product-image,
html .hero-scene[data-mode="video"] .hero-product-ghost,
html .hero-scene[data-mode="video"].is-ready:not(.is-idle) .hero-still-stack,
html .hero-scene[data-mode="video"].is-ready:not(.is-idle) .hero-product-image,
html .hero-scene[data-mode="video"].is-ready:not(.is-idle) .hero-product-ghost,
html .hero-scene[data-mode="video"].is-landed .hero-still-stack,
html .hero-scene[data-mode="video"].is-landed .hero-product-image,
html .hero-scene[data-mode="video"].is-landed .hero-product-ghost,
html .hero-scene[data-mode="ugc"] .hero-still-stack,
html .hero-scene[data-mode="ugc"] .hero-product-image,
html .hero-scene[data-mode="ugc"] .hero-product-ghost,
html .hero-scene[data-mode="ugc"].is-ready:not(.is-idle) .hero-still-stack,
html .hero-scene[data-mode="ugc"].is-ready:not(.is-idle) .hero-product-image,
html .hero-scene[data-mode="ugc"].is-ready:not(.is-idle) .hero-product-ghost,
html .hero-scene[data-mode="ugc"].is-landed .hero-still-stack,
html .hero-scene[data-mode="ugc"].is-landed .hero-product-image,
html .hero-scene[data-mode="ugc"].is-landed .hero-product-ghost {
  display: none !important;
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}
html .hero-scene[data-mode="3d"] .hero-haze,
html .hero-scene[data-mode="ar"] .hero-haze,
html .hero-scene[data-mode="3d"] .hero-roulette-loader,
html .hero-scene[data-mode="ar"] .hero-roulette-loader,
html .hero-scene[data-mode="configurator"] .hero-roulette-loader {
  display: none !important;
  opacity: 0 !important;
}
html .hero-scene[data-mode="3d"] .hero-model-frame,
html .hero-scene[data-mode="3d"].is-model-ready .hero-model-frame,
html .hero-scene[data-mode="3d"] .hero-media-layer.hero-model-frame,
html .hero-scene[data-mode="ar"] .hero-model-frame,
html .hero-scene[data-mode="ar"].is-model-ready .hero-model-frame,
html .hero-scene[data-mode="ar"] .hero-media-layer.hero-model-frame {
  -webkit-mask-image: none !important;
  mask-image: none !important;
}
html .hero-scene[data-mode="3d"] .hero-model-frame,
html .hero-scene[data-mode="ar"] .hero-model-frame {
  display: grid !important;
  overflow: visible !important;
  opacity: 1 !important;
  visibility: visible !important;
  pointer-events: none;
}
html .hero-scene[data-mode="3d"] .hero-model-frame::after,
html .hero-scene[data-mode="ar"] .hero-model-frame::after {
  content: none;
}
.hero-ar-shell {
  position: absolute;
  inset: 0;
  overflow: visible;
  pointer-events: none;
}
.hero-scene:not([data-mode="3d"]):not([data-mode="ar"]) .hero-ar-shell {
  display: none;
}
.hero-scene[data-mode="3d"] .hero-ar-shell,
.hero-scene[data-mode="ar"] .hero-ar-shell {
  left: 50%;
  top: 50%;
  right: auto;
  bottom: auto;
  z-index: 5;
  width: min(28vw, 340px);
  aspect-ratio: .72;
  transform: translate(-50%, -50%);
}
.hero-ar-device {
  position: absolute;
  inset: 0;
  background: transparent;
  pointer-events: none;
}
.hero-scene[data-mode="3d"] .hero-ar-device,
.hero-scene[data-mode="ar"] .hero-ar-device {
  overflow: hidden;
  isolation: isolate;
  border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
  border-radius: 28px;
  box-shadow: inset 0 0 0 9px color-mix(in srgb, var(--surface) 70%, transparent), 0 18px 36px rgba(17, 19, 15, .08);
}
.hero-scene:not([data-mode="ar"]) .hero-ar-launch,
.hero-scene:not([data-mode="ar"]) .hero-ar-qr { display: none; }
.hero-scene:not([data-mode="3d"]):not([data-mode="ar"]) .hero-ar-spinner { display: none; }
.hero-scene:not([data-mode="3d"]):not([data-mode="ar"]) .hero-ar-device::before { display: none; }
.hero-scene[data-mode="3d"] .hero-ar-device::before,
.hero-scene[data-mode="ar"] .hero-ar-device::before {
  position: absolute;
  left: 50%;
  top: 11px;
  z-index: 2;
  width: 18%;
  height: 5px;
  border-radius: 99px;
  background: color-mix(in srgb, var(--ink) 34%, transparent);
  transform: translateX(-50%);
  content: "";
}
.hero-scene[data-mode="3d"] .hero-config-frame,
.hero-scene[data-mode="ar"] .hero-config-frame {
  display: block !important;
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}
.hero-scene[data-mode="3d"] .hero-ar-device .hero-config-canvas,
.hero-scene[data-mode="ar"] .hero-ar-device .hero-config-canvas {
  position: absolute;
  inset: 12px 9px 22px;
  z-index: 1;
  display: block;
  width: auto;
  height: auto;
  opacity: 0;
  background: transparent;
  pointer-events: auto;
}
.hero-scene[data-mode="3d"].is-chair-ready .hero-ar-device .hero-config-canvas,
.hero-scene[data-mode="ar"].is-chair-ready .hero-ar-device .hero-config-canvas {
  opacity: 1;
}
.hero-scene[data-mode="3d"] .hero-ar-device model-viewer,
.hero-scene[data-mode="ar"] .hero-ar-device model-viewer {
  position: absolute !important;
  left: 0;
  top: 0;
  width: 1px !important;
  height: 1px !important;
  overflow: hidden;
  opacity: 0 !important;
  pointer-events: none !important;
}
.hero-orbit-hint {
  position: absolute;
  left: 50%;
  top: 48%;
  z-index: 8;
  display: none;
  color: #fff;
  pointer-events: none;
  filter: drop-shadow(0 1px 2px rgba(16, 22, 34, .35));
  transform: translate(-50%, -50%);
  opacity: 0;
}
.hero-orbit-hint.is-on {
  display: block;
  animation: hero-orbit-hint 2s ease forwards;
}
.hero-orbit-hint svg {
  display: block;
  width: 64px;
  height: 36px;
}
.hero-orbit-hint path { fill: none; }
html[data-skin="nocturne"] .hero-orbit-hint {
  color: #fff;
  filter: drop-shadow(0 1px 3px rgba(8, 14, 28, .5));
}
@keyframes hero-orbit-hint {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(.96); }
  12% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  78% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
}
.hero-ar-launch {
  position: absolute;
  right: 14px;
  bottom: 16px;
  z-index: 6;
  display: grid;
  width: 36px;
  height: 36px;
  place-items: center;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--ink) 18%, transparent);
  border-radius: 50%;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  color: var(--ink);
  cursor: pointer;
  pointer-events: auto;
}
.hero-ar-launch svg {
  width: 18px;
  height: 18px;
}
.hero-scene[data-mode="3d"]:not(.is-chair-ready):not(.is-chair-failed) .hero-ar-spinner,
.hero-scene[data-mode="ar"]:not(.is-chair-ready):not(.is-chair-failed) .hero-ar-spinner {
  display: grid;
}
.hero-ar-corners {
  display: none;
  pointer-events: none;
}
.hero-scene[data-mode="3d"] .hero-ar-corners,
.hero-scene[data-mode="ar"] .hero-ar-corners {
  position: absolute;
  z-index: 7;
  display: block;
  top: 12%;
  bottom: 13%;
  left: -18%;
  right: -18%;
  border: 1.5px solid #8cb4ff;
  border-radius: 0;
  clip-path: none;
  box-sizing: border-box;
  box-shadow: 0 0 6px rgba(140, 180, 255, .36), 0 0 14px rgba(140, 180, 255, .14);
}
html[data-skin="nocturne"] .hero-scene[data-mode="3d"] .hero-ar-corners,
html[data-skin="nocturne"] .hero-scene[data-mode="ar"] .hero-ar-corners {
  border-color: #9ec4ff;
  box-shadow: 0 0 7px rgba(158, 196, 255, .4), 0 0 16px rgba(158, 196, 255, .16);
}
.hero-scene[data-mode="3d"] model-viewer::part(default-progress-bar),
.hero-scene[data-mode="3d"] model-viewer::part(default-ar-button),
.hero-scene[data-mode="ar"] model-viewer::part(default-progress-bar),
.hero-scene[data-mode="ar"] model-viewer::part(default-ar-button) {
  display: none;
}
.hero-ar-qr {
  position: absolute;
  right: 12px;
  bottom: 58px;
  z-index: 8;
  display: none;
  width: 168px;
  padding: 10px 10px 8px;
  border: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  border-radius: 14px;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  box-shadow: 0 10px 24px rgba(17, 19, 15, .12);
  color: var(--ink);
  text-align: center;
  pointer-events: auto;
}
.hero-scene[data-mode="ar"] .hero-ar-device:has(.hero-ar-qr.is-on) { overflow: visible; }
.hero-ar-qr.is-on { display: grid; gap: 6px; }
.hero-ar-qr p {
  margin: 0;
  font-size: .58rem;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.hero-ar-qr-canvas {
  display: block;
  width: 148px;
  height: 148px;
  margin: 0 auto;
  border-radius: 8px;
}
.hero-ar-qr-url {
  display: none !important;
}
.hero-ar-qr-close {
  padding: 2px 0 0;
  border: 0;
  background: none;
  color: var(--ink);
  cursor: pointer;
  font: inherit;
  font-size: .58rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.hero-ar-spinner {
  background: transparent;
}
.hero-config-spinner,
.hero-ar-spinner {
  background: transparent;
  box-shadow: none;
}

@media (min-width: 861px) {
  .menu-button { display: none !important; }

  /* One viewport: title + lede, leftover media, readout + CTA on the floor. */
  html .hero.has-sky,
  html .hero.has-sky-fallback {
    height: 100svh;
    min-height: 100svh;
    max-height: 100svh;
    padding-top: var(--header, 72px);
    padding-bottom: 18px;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
  }
  html .hero.has-sky .hero-grid,
  html .hero.has-sky-fallback .hero-grid {
    flex: 1 1 auto;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    width: 100%;
    min-height: 0;
    height: auto;
  }
  html .hero.has-sky .hero-copy,
  html .hero.has-sky-fallback .hero-copy {
    min-height: 0;
  }
  html .hero.has-sky h1 {
    margin-bottom: 0;
  }
  html .hero.has-sky h1 span:last-child {
    margin-left: .16em;
  }
  html .hero.has-sky .hero-intro {
    margin-top: 0 !important;
  }
  html .hero.has-sky .hero-showcase-wrap,
  html .hero.has-sky-fallback .hero-showcase-wrap {
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    min-height: 0;
    max-height: none;
    height: 100%;
    margin-top: 0;
  }
  html .hero.has-sky .hero-showcase,
  html .hero.has-sky-fallback .hero-showcase {
    min-height: 0;
    height: 100%;
  }
  html .hero.has-sky .hero-actions,
  html .hero.has-sky-fallback .hero-actions {
    position: relative !important;
    left: auto !important;
    bottom: auto !important;
    transform: none !important;
    margin-top: 8px !important;
    margin-bottom: 0 !important;
    gap: 4px !important;
  }
  html .hero.has-sky .hero-actions .hero-readout {
    margin: 0 !important;
    top: 0 !important;
  }
  html .hero.has-sky .hero-product-stage,
  html .hero.has-sky-fallback .hero-product-stage {
    top: 50%;
    width: min(56vw, 94%, 840px);
    height: 96%;
    max-height: 96%;
    aspect-ratio: auto;
  }
  /* Films / 3D / Imagery: the leftover-svh stage was the square the user sees. */
  html .hero.has-sky .hero-scene[data-mode="video"] .hero-product-stage,
  html .hero.has-sky .hero-scene[data-mode="3d"] .hero-product-stage,
  html .hero.has-sky .hero-scene[data-mode="imagery"] .hero-product-stage,
  html .hero.has-sky-fallback .hero-scene[data-mode="video"] .hero-product-stage,
  html .hero.has-sky-fallback .hero-scene[data-mode="3d"] .hero-product-stage,
  html .hero.has-sky-fallback .hero-scene[data-mode="imagery"] .hero-product-stage {
    width: min(45vw, 620px);
    height: auto !important;
    max-height: none !important;
    aspect-ratio: 5 / 3;
    overflow: hidden;
    /* Softer corner on the desktop plate. The phone keeps 18px against a much
       smaller card — the same radius there would start eating the picture. */
    border-radius: 28px;
  }
  html .hero.has-sky .hero-scene[data-mode="video"] .hero-media-visual,
  html .hero.has-sky .hero-scene[data-mode="3d"] .hero-media-visual,
  html .hero.has-sky .hero-scene[data-mode="imagery"] .hero-media-visual,
  html .hero.has-sky-fallback .hero-scene[data-mode="video"] .hero-media-visual,
  html .hero.has-sky-fallback .hero-scene[data-mode="3d"] .hero-media-visual,
  html .hero.has-sky-fallback .hero-scene[data-mode="imagery"] .hero-media-visual {
    inset: 0 !important;
  }
  .hero-scene[data-mode="configurator"] {
    --chair-stage-w: min(50vw, 70%, 720px);
    --chair-ui-inset: clamp(46px, calc(2.4vw + 24px), 60px);
  }
  html .hero.has-sky .hero-scene[data-mode="configurator"] .hero-product-stage,
  html .hero.has-sky-fallback .hero-scene[data-mode="configurator"] .hero-product-stage {
    top: 50%;
    width: var(--chair-stage-w);
    height: 96%;
    max-height: 96%;
    aspect-ratio: auto;
    overflow: hidden;
  }
  .hero-scene[data-mode="configurator"] .hero-media-visual,
  .hero-scene[data-mode="configurator"] .hero-config-frame {
    inset: 0;
    overflow: hidden;
  }
  html .hero.has-sky .hero-scene[data-mode="3d"] .hero-product-stage,
  html .hero.has-sky .hero-scene[data-mode="ar"] .hero-product-stage {
    overflow: visible;
  }
  html .hero.has-sky .hero-scene[data-mode="3d"] .hero-ar-shell,
  html .hero.has-sky .hero-scene[data-mode="ar"] .hero-ar-shell {
    top: 50%;
    width: auto;
    height: min(88%, 640px);
    max-width: min(28vw, 400px);
    aspect-ratio: .72;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-config-canvas,
  .hero-chair-ui { transition: none; }
  .hero-load-bar::after,
  .hero-config-bar::after { animation: none; width: calc(100% - 2px); opacity: .7; }
  .hero-orbit-hint,
  .hero-orbit-hint.is-on { display: none !important; animation: none; }
}

@media (max-width: 1100px) and (min-width: 861px) {
  .hero-scene[data-mode="configurator"] { --chair-ui-inset: clamp(44px, calc(3.4vw + 24px), 60px); }
}

@media (max-width: 860px) {
  .hero-scene[data-mode="configurator"] {
    /* The controls hang off the plate's edges, so they read off the same width
       the plate is actually drawn at rather than a second guess at it. */
    --chair-stage-w: var(--hero-plate-w, min(58vw, 250px));
    /* Positive pulls the columns inboard of the plate edge. They were pushed
       out by 4px, which was fine against a 270px plate and clipped the material
       pills once it grew to 303. */
    --chair-ui-inset: 4px;
  }
  .hero-chair-ui {
    position: absolute;
    inset: 0;
    z-index: 8;
    display: none;
    margin: 0;
    padding: 0;
  }
  .hero-scene[data-mode="configurator"]:not(.is-roulette):not(.is-preparing):not(.is-media-locked).is-chair-ready .hero-chair-ui {
    display: block;
  }
  /* The columns used to start at 30% of the scene, which put every swatch on top
     of the chair itself — the thing they are meant to be changing. There is a
     clear band above the seat, under the title, so they go there instead, and
     out toward the edges rather than over the armrests. They start higher again
     now that Show input has moved off the top-right corner they were sharing. */
  .hero-chair-col,
  .hero-chair-col--left,
  .hero-chair-col--right {
    position: absolute;
    top: 5%;
    gap: 10px;
  }
  /* Show input sits in the plate's bottom-right corner in every mode, opposite
     the pause control in the bottom-left. It is moved inside the stage on a
     phone (see placeHeroUtility in app.js), so these offsets are the plate's own
     edges rather than the scene's — which is why the same numbers land in the
     same place whatever the screen height. */
  html .hero-scene .hero-utility {
    top: auto !important;
    bottom: 12px !important;
    right: 12px !important;
    left: auto !important;
    /* The entrance parks this 12px low and the pane can leave it there; the
       plate corner is a fixed position, not something to animate into. */
    transform: none !important;
    z-index: 14;
  }
  html .hero-scene .hero-input-toggle {
    min-height: 26px;
    /* A flex pill with default alignment leaves the label sitting at the top of
       its own box; the mono font's line box is well short of the 26px the pill
       is held to. Centre it on both axes and let the padding be symmetrical. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 10px;
    font-size: .42rem;
    line-height: 1;
    letter-spacing: .07em;
  }
  html .hero-scene .hero-utility {
    align-items: center;
  }
  /* Base finishes stack. Side by side the pair was wider than the column that
     holds it and ran out past the plate's right edge. */
  /* The base finishes stack to match the material pills beside them, at the same
     size and the same gap — otherwise the two groups drift apart down the card
     and Base reads as sitting lower than Material. */
  .hero-scene[data-mode="configurator"] .hero-chair-bases {
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
  }
  .hero-scene[data-mode="configurator"] .hero-chair-pills { gap: 6px; }
  .hero-scene[data-mode="configurator"] .hero-chair-base { width: 28px; height: 28px; }
  .hero-scene[data-mode="configurator"] .hero-chair-pill { min-height: 26px; padding: 0 7px; }
  .hero-chair-col--left {
    left: calc(50% - (var(--chair-stage-w) / 2) + var(--chair-ui-inset));
    transform: translate(-50%, 0);
  }
  .hero-chair-col--right {
    left: calc(50% + (var(--chair-stage-w) / 2) - var(--chair-ui-inset));
    transform: translate(-50%, 0);
  }
  .hero-chair-group--lower { margin-top: 14px; }
  .hero-chair-pills { flex-direction: column; gap: 6px; }
  .hero-chair-tip { display: none; }
  .hero-chair-swatch { width: 24px; height: 24px; }
  .hero-chair-pill { min-height: 28px; padding: 0 7px; font-size: .44rem; letter-spacing: .06em; }
  .hero-chair-base { width: 32px; height: 32px; }
  /* The handset and the configurator stage used to be sized here off
     --hero-stage-h, which is how they each ended up with a box of their own.
     The shared plate above owns both now. */
}

@media (max-width: 560px) {
  .hero-chair-label { font-size: .48rem; }
  .hero-chair-pills { gap: 6px; }
  .hero-chair-pill { padding: 0 9px; }
}

html .hero-scene[data-mode="3d"] .hero-ar-shell,
html .hero-scene[data-mode="3d"] .hero-model-frame,
html .hero-scene[data-mode="3d"] .hero-ar-device,
html .hero-scene[data-mode="3d"] .hero-ar-spinner,
html .hero.has-sky .hero-scene[data-mode="3d"] .hero-ar-shell {
  display: none !important;
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
}

@keyframes hero-load-fill {
  0% { width: 0; }
  72% { width: calc(100% - 2px); }
  100% { width: calc(100% - 2px); }
}
@keyframes hero-load-shimmer {
  0% { transform: translateX(-130%); }
  100% { transform: translateX(360%); }
}
@keyframes hero-chair-bar {
  0% { transform: translateX(-130%); }
  100% { transform: translateX(360%); }
}

/* A short phone has no room for a tall plate: the hero already has a title, the
   mode list and the call to action to fit above the fold, so the portrait crop
   gives height back rather than pushing them off screen. */
@media (max-width: 860px) and (max-height: 700px) {
  html .hero .hero-scene {
    --hero-plate-h: min(36svh, 260px);
  }
  /* A short phone gives the plate 230px, and the chair controls were sized for
     a plate half as tall again — the stack ran off the bottom of it and into the
     service list. Everything in the column tightens rather than the plate
     growing, because the plate is the one thing that has to match every mode. */
  .hero-scene[data-mode="configurator"] .hero-chair-col,
  .hero-scene[data-mode="configurator"] .hero-chair-col--left,
  .hero-scene[data-mode="configurator"] .hero-chair-col--right {
    gap: 6px;
  }
  .hero-scene[data-mode="configurator"] .hero-chair-group--lower { margin-top: 8px; }
  .hero-scene[data-mode="configurator"] .hero-chair-swatches { gap: 6px; }
  .hero-scene[data-mode="configurator"] .hero-chair-bases { gap: 6px; }
  .hero-scene[data-mode="configurator"] .hero-chair-swatch { width: 20px; height: 20px; }
  .hero-scene[data-mode="configurator"] .hero-chair-base { width: 26px; height: 26px; }
}

/* ── Showing the input photos ────────────────────────────────────────────────
   "Show input" is meant to put the source photography on the stage. It was
   fading the render to .16 rather than clearing it, so the chair stayed legible
   through the overlay and the two read as a double exposure. Nothing of the
   render is shown while the input is open; it comes back untouched on close.

   The chair controls were supposed to go with it — that rule already existed,
   it was just losing to the more specific one that keeps them up in
   configurator mode, so the swatches sat over the input photos at full colour
   while being unclickable. Stated at matching weight here, it lands. */
html .hero-scene.is-source-visible .hero-product-stage,
html .hero-scene.is-source-visible .hero-play,
html .hero-scene.is-source-visible .hero-swatch-rail,
html .hero-scene.is-source-visible .hero-viewfinder,
html .hero-scene.is-source-visible .hero-haze {
  opacity: 0 !important;
  pointer-events: none !important;
}
html .hero-scene[data-mode="configurator"].is-source-visible .hero-chair-ui,
html .hero-scene[data-mode="configurator"].is-source-visible.is-chair-ready .hero-chair-ui {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}
/* The service list is greyed out while the input is open, and the buttons
   inside it set pointer-events back to auto for their own hover states — so the
   list was inert and every button in it was still clickable. */
html .hero-scene.is-source-visible .hero-services,
html .hero-scene.is-source-visible .hero-services .hero-mode,
html .hero-scene.is-source-visible .hero-services .hero-group {
  pointer-events: none !important;
}

/* ── The pause control ───────────────────────────────────────────────────────
   It sat dead centre, on top of the product — the one part of the frame that
   has to stay clear. It moves to the bottom-left corner of the plate, inset by
   the same amount from both edges, where it reads as a control rather than a
   badge stamped on the film. */
html .hero-scene[data-mode="video"] .hero-play,
html .hero-scene[data-mode="ugc"] .hero-play,
html .hero-scene[data-mode="3d"] .hero-play {
  left: 14px !important;
  right: auto !important;
  top: auto !important;
  bottom: 14px !important;
  width: 30px !important;
  opacity: .42;
  border-color: color-mix(in srgb, var(--paper) 30%, transparent);
  background: color-mix(in srgb, rgb(12 14 18) 34%, transparent);
  box-shadow: none;
  transform: none !important;
  transition: opacity 180ms ease;
}
html .hero-scene[data-mode="video"] .hero-play::after,
html .hero-scene[data-mode="ugc"] .hero-play::after,
html .hero-scene[data-mode="3d"] .hero-play::after {
  border-left-color: var(--paper);
  border-top-width: 5px;
  border-bottom-width: 5px;
  border-left-width: 8px;
  margin-left: 2px;
}
html .hero-scene[data-mode="video"] .hero-play[aria-pressed="true"]::after,
html .hero-scene[data-mode="ugc"] .hero-play[aria-pressed="true"]::after,
html .hero-scene[data-mode="3d"] .hero-play[aria-pressed="true"]::after {
  width: 8px;
  height: 10px;
  margin: 0;
  background: linear-gradient(90deg, var(--paper) 0 34%, transparent 34% 66%, var(--paper) 66% 100%);
}
/* It is a control, not a badge: quiet until you go looking for it, then plainly
   there. Hover and keyboard focus bring it back to full strength. */
html .hero-scene[data-mode="video"] .hero-play:hover,
html .hero-scene[data-mode="ugc"] .hero-play:hover,
html .hero-scene[data-mode="3d"] .hero-play:hover,
html .hero-scene[data-mode="video"] .hero-play:focus-visible,
html .hero-scene[data-mode="ugc"] .hero-play:focus-visible,
html .hero-scene[data-mode="3d"] .hero-play:focus-visible {
  opacity: 1;
}
@media (max-width: 860px) {
  /* Pause and Show input are a pair, one in each bottom corner of the plate:
     same size, same inset from their own edge, so their centres sit on one
     line. They were 10/10 at 26px against 14/14 at 22px, which is near enough
     to look like a mistake rather than a choice. */
  html .hero-scene[data-mode="video"] .hero-play,
  html .hero-scene[data-mode="ugc"] .hero-play,
  html .hero-scene[data-mode="3d"] .hero-play {
    left: 12px !important;
    bottom: 12px !important;
    width: 26px !important;
  }
}

/* ── Base finishes ───────────────────────────────────────────────────────────
   Both buttons were the same pale circle with the same five-spoke glyph, and
   the only thing telling them apart was which one happened to be filled with
   ink for the selected state. That is not a choice between a silver base and a
   black one — it is two identical controls. Each now carries its own finish, so
   the button looks like the part it selects, and selection is shown with a ring
   around the finish rather than by painting over it. */
.hero-chair-base[data-chair-base="silver"] {
  border-color: color-mix(in srgb, var(--ink) 24%, transparent);
  background: linear-gradient(150deg, #fbfbfc 6%, #d9dde2 48%, #a9afb8 100%);
  color: #545b66;
}
.hero-chair-base[data-chair-base="black"] {
  border-color: color-mix(in srgb, var(--ink) 34%, transparent);
  background: linear-gradient(150deg, #4a4f57 6%, #23262b 48%, #0d0f12 100%);
  color: #e7eaee;
}
html .hero-chair-base[data-chair-base="silver"][aria-pressed="true"],
html .hero-chair-base[data-chair-base="black"][aria-pressed="true"] {
  border-color: var(--hero-accent, var(--ink));
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--hero-accent, var(--ink)) 60%, transparent);
}
html .hero-chair-base[data-chair-base="silver"][aria-pressed="true"] {
  background: linear-gradient(150deg, #fbfbfc 6%, #d9dde2 48%, #a9afb8 100%);
  color: #545b66;
}
html .hero-chair-base[data-chair-base="black"][aria-pressed="true"] {
  background: linear-gradient(150deg, #4a4f57 6%, #23262b 48%, #0d0f12 100%);
  color: #e7eaee;
}

/* Desktop only: the plate's corner is softer than the phone's, and everything
   drawn inside it has to carry the same radius or a square corner shows at the
   edge of the video where the plate has already curved away. */
@media (min-width: 861px) {
  html .hero-scene[data-mode="video"] .hero-video-frame,
  html .hero-scene[data-mode="3d"] .hero-video-frame,
  html .hero-scene[data-mode="ugc"] .hero-video-frame,
  html .hero-scene[data-mode="video"] .hero-video-frame video,
  html .hero-scene[data-mode="3d"] .hero-video-frame video,
  html .hero-scene[data-mode="ugc"] .hero-video-frame video,
  html .hero-scene[data-mode="imagery"] .hero-product-image,
  html .hero-scene[data-mode="imagery"] .hero-product-ghost {
    border-radius: 28px !important;
  }
  html .hero.has-sky .hero-scene[data-mode="ugc"] .hero-product-stage,
  html .hero.has-sky-fallback .hero-scene[data-mode="ugc"] .hero-product-stage {
    border-radius: 28px;
  }
}

/* The AR placement frame is drawn 18% wider than the media on each side, which
   reads as a scanner overshooting the subject. That works on a desktop where the
   frame sits inside a stage with room around it; on a phone the plate clips at
   its own edge, so the frame's left and right sides were simply cut off and the
   graphic read as broken. Inside the plate on a phone, with the same overshoot
   expressed as a small inset instead. */
@media (max-width: 860px) {
  html .hero-scene[data-mode="3d"] .hero-ar-corners,
  html .hero-scene[data-mode="ar"] .hero-ar-corners {
    left: 10px;
    right: 10px;
    top: 14%;
    bottom: 15%;
  }
}

/* The loader while a film is still arriving. It already existed for the
   roulette spin; this is the other time the plate has nothing to show, and
   without it a cold service looks like a button that did not work. */
html .hero-scene.is-media-loading .hero-roulette-loader {
  display: grid !important;
  opacity: .88 !important;
  transform: translate(-50%, -50%) scale(1) !important;
}
html .hero-scene.is-media-loading .hero-video-frame video {
  opacity: 0;
  transition: opacity 180ms ease;
}

/* -------------------------------------------------------------------------
   Mode switching on a phone: a cut, not a production.

   The reveal choreography was written for the desktop plate, where the media
   scales up out of a blur as it resolves. On a phone it falls apart, and
   measuring a switch shows why: this file already pins .hero-media-visual to
   transform: none !important on mobile (so every mode shares one plate), and
   an !important declaration beats a keyframe. The scale half of
   hero-media-resolve therefore never renders. What survives is the half that
   should not: opacity .08 behind blur(7px), held for the animation's full
   920ms and then dropped in a single frame. Measured on a switch to 3D, the
   card sat at opacity .08 / blur(7px) from 128ms to 860ms and was back at
   opacity 1 by 1000ms with nothing in between -- which is exactly the "gets
   stuck, then becomes big" it looks like.

   Rather than re-tune a transform that cannot apply, mobile opts out of the
   whole sequence and cross-fades. The loader still covers media that is
   genuinely not buffered yet (is-media-loading owns the video's own opacity,
   which is untouched here), so a slow network still gets feedback -- it just
   no longer pretends to be a reveal when the file is already in memory.
   ------------------------------------------------------------------------- */
@media (max-width: 860px) {
  html .hero .hero-scene.is-revealing .hero-media-visual,
  html .hero .hero-scene.is-resolving .hero-media-visual,
  html .hero .hero-scene.is-switching .hero-media-visual,
  html .hero .hero-scene.is-preparing .hero-media-visual,
  html .hero .hero-scene.is-roulette .hero-media-visual {
    animation: none !important;
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
  }

  /* The plate itself was dimming and blurring underneath the media, which put
     a second fade on top of the first and made the card look like it was
     failing to load. */
  html .hero .hero-scene.is-revealing .hero-product-stage,
  html .hero .hero-scene.is-resolving .hero-product-stage,
  html .hero .hero-scene.is-switching .hero-product-stage,
  html .hero .hero-scene.is-preparing .hero-product-stage,
  html .hero .hero-scene.is-roulette .hero-product-stage {
    opacity: 1 !important;
    filter: none !important;
  }

  /* One short fade carries the change of content. Short enough that a tap
     feels answered immediately, long enough not to read as a hard cut. */
  html .hero .hero-scene .hero-media-visual {
    transition: opacity 180ms ease !important;
  }

  /* The layers inside the plate cross-fade on the same beat, so a switch
     between two already-loaded modes never shows the plate empty. */
  html .hero .hero-scene .hero-media-layer {
    transition: opacity 180ms ease !important;
  }
}

/* -------------------------------------------------------------------------
   "Show input" sits under the plate, not on it.

   It used to be moved inside the media stage on a phone and pinned to the
   plate's bottom-right corner. That put it on top of the product in every
   mode -- over the chair in the configurator, over the film everywhere else --
   which is the one thing a control named "show input" should not cover. It now
   stays a child of the scene at every width (see placeHeroUtility in app.js)
   and takes its own row between the plate and the service list, right-aligned
   to the plate's edge with a clear gap above and below, so it never touches
   the card and never sits over a service row.
   ------------------------------------------------------------------------- */
@media (max-width: 860px) {
  html .hero .hero-scene .hero-utility {
    position: static;
    inset: auto;
    /* The scene is a grid and the utility is appended last, so without this it
       would land under the service list. Only the plate, this and the list are
       in flow; everything else in the scene is absolutely positioned. */
    order: 0;
    z-index: auto;
    /* The scene's grid is two columns wide for the desktop rail, so without
       this the chip is laid into column one and right-aligns to the middle of
       the plate instead of its edge. */
    grid-column: 1 / -1;
    display: flex;
    justify-content: flex-end;
    width: 100%;
    margin: 0;
    /* The plate is a couple of pixels wider than the scene's content box, so
       the chip is nudged out to line up with the card's right edge. */
    padding-right: 0;
    transform: none !important;
    /* The row is only as wide as the chip for pointer purposes; an empty strip
       across the full width would sit over nothing, but it costs nothing to be
       explicit now that this element is in the tap path. */
    pointer-events: none;
  }

  html .hero .hero-scene .hero-utility > * { pointer-events: auto; }

  html .hero .hero-scene .hero-services { order: 1; }

  /* Much smaller than the in-card version: it is now on the page background
     rather than over the media, so it no longer has to fight an image for
     contrast and can afford to be quiet. */
  html .hero .hero-scene .hero-utility .hero-input-toggle {
    min-height: 20px;
    padding: 0 9px;
    font-size: 6px;
    letter-spacing: .05em;
    border-radius: 10px;
    transform: none !important;
  }
}


/* -------------------------------------------------------------------------
   The video fills its frame in every mode, not just three of them.

   styles.css sizes .hero-video-frame video at 94% x 88% and then overrides it
   to 100% for data-mode video / 3d / ugc only. The same <video> element is
   reused for every mode, so in any other state it sits at the 94/88 fallback --
   and switching into a video mode makes it jump from 94/88 to 100 at the
   moment it is revealed. Measured on a switch to UGC: 340.4x382.8 becoming
   362.1x435, no transform anywhere, a pure layout step. That is the "scales to
   100" -- the card does not zoom, the video inside it changes size underneath
   the fade.

   Keying the fill on the frame instead of the mode removes the step: the
   element is the same size before, during and after, so a switch is a change
   of pixels only. Opacity still carries the change, which is what a phone
   should do -- show the next thing, not perform its arrival.
   ------------------------------------------------------------------------- */
@media (max-width: 860px) {
  html .hero .hero-scene .hero-video-frame video {
    position: relative !important;
    inset: auto !important;
    width: 100% !important;
    height: 100% !important;
    max-height: none !important;
    min-height: 0 !important;
    object-fit: cover !important;
    object-position: center !important;
    /* Only opacity may animate. Anything geometric here is the bug above. */
    transition: opacity 180ms ease !important;
  }

  /* The headline sat 13.6px below the nav and only 4px above the card, so the
     gap above it read as slack while the card looked cramped. Pulling the copy
     up evens the two and hands the difference to the media. */
  html .hero .container.hero-grid .hero-copy {
    margin-top: -8px;
  }
}

/* -------------------------------------------------------------------------
   Big screens get a bigger plate.

   The desktop plate is min(45vw, 620px), so past about 1380px wide the 620
   cap takes over and the card stays exactly the same size no matter how much
   monitor there is. Measured at 1920x1080 it sat at 620x372 with 384px of
   empty page either side and ~212px above and below -- small in the middle of
   a lot of nothing, which is what it looks like.

   Three things constrain how far it can grow, and all three are respected:

     - The service labels. Their inner edges are at 266 and 1654 at 1920, so a
       900px plate still leaves 244px a side for the connector lines to read as
       lines rather than nicks.
     - The fold. The third term keeps the plate's 5:3 height inside the room
       between the copy and the mode readout, so a wide-but-short window
       (1600x800) shrinks it rather than pushing the readout off screen.
     - The configurator. Its swatch columns are anchored off --chair-stage-w,
       which was separately hardcoded to the same 620. Both now read one
       value, so the columns stay on the plate's edges instead of floating
       inside a plate that grew without them.

   Below 1600px nothing changes: the vw arm was already binding there, which is
   why smaller screens look right as they are.
   ------------------------------------------------------------------------- */
@media (min-width: 1600px) {
  html .hero .hero-scene {
    --hero-desk-plate-w: min(48vw, 900px, calc((100svh - 430px) * 5 / 3));
  }

  /* The per-mode desktop rule above (.hero.has-sky .hero-scene[data-mode=...]
     .hero-product-stage) carries five class/attribute components and beats a
     plainer selector, so this one is marked rather than padded out with
     selectors nobody can read. It is the only width that applies at this
     width -- the mobile rule is inside max-width: 860px -- so there is nothing
     for it to fight. */
  html .hero .hero-scene .hero-product-stage {
    width: var(--hero-desk-plate-w) !important;
  }

  html .hero .hero-scene[data-mode="configurator"] {
    --chair-stage-w: var(--hero-desk-plate-w);
  }
}

/* -------------------------------------------------------------------------
   The card was never actually bigger on a phone.

   The plate height is the viewport minus the chrome, and I had been sizing
   that against a 852px viewport -- the full screen. Safari shows its own
   chrome, so 100svh on that same phone is nearer 750, and there the old
   numbers produced 291 x 350: smaller than the 303 x 364 it replaced. The
   measurement was right and the viewport was wrong, which is why it kept
   looking unchanged.

   On a 750px viewport the stack under the plate came to 280px and the copy
   above it ~120, leaving 350 for the media. The only way the card gets bigger
   is for that 400 to get smaller, so the three most generous pieces give a
   little back:

     the mode readout   17px/31px line with 14px margins -> 15px/1.35 with 9px
     the service rows   38px min-height each, three of them -> 34px
     the call to action 58px tall -> 50px

   That is about 50px, and the plate's overhead constant drops by the same
   amount so the space lands on the media rather than turning into a gap.
   Nothing here is small enough to hurt a tap target: 34px rows and a 50px
   button both clear the 44px guideline once their padding is counted, and the
   readout is copy, not a control.
   ------------------------------------------------------------------------- */
@media (max-width: 860px) {
  html .hero .hero-scene {
    --hero-plate-h: min(calc(100svh - 370px), 448px);
  }

  /* mobile-refine.css pins this with html .hero.has-sky .hero-actions
     .hero-readout { margin: 14px 0 !important }, so the margin has to be
     claimed at that weight or it simply does not move. The two-line height
     stays: these descriptions do wrap to two lines on a phone, and reserving
     the second is what stops the list below jumping as you switch service. */
  html .hero .hero-readout,
  html .hero .hero-mode-note,
  html .hero.has-sky .hero-actions .hero-readout,
  html .hero.has-sky-fallback .hero-actions .hero-readout {
    font-size: 15px !important;
    line-height: 1.35 !important;
    margin-block: 9px !important;
  }

  html .hero .hero-services .hero-mode {
    min-height: 34px !important;
    padding-top: 2px !important;
    padding-bottom: 2px !important;
  }

  /* The call to action sits in .hero-actions, not .hero-copy. 50px still
     clears a comfortable tap target. */
  html .hero .hero-actions .button {
    min-height: 50px !important;
    padding-top: 13px !important;
    padding-bottom: 13px !important;
  }
}

/* -------------------------------------------------------------------------
   No scaling on a phone. One thing replaces another.

   The reveal choreography has been trimmed twice already and this stage still
   reads as expanding when a service is tapped. Rather than keep chasing which
   layer is doing it, every media layer inside the plate is denied animation
   and transform outright, and the only thing left that may change is opacity.
   A switch is now a cross-fade between two things of identical size: the
   outgoing layer goes, the incoming one arrives, and nothing grows.

   Scoped to the media layers by name rather than to the plate's whole subtree,
   so the loader and the chair's own controls keep the animation they need.
   ------------------------------------------------------------------------- */
@media (max-width: 860px) {
  html .hero .hero-product-stage .hero-media-visual,
  html .hero .hero-product-stage .hero-media-layer,
  html .hero .hero-product-stage .hero-still-stack,
  html .hero .hero-product-stage .hero-video-frame,
  html .hero .hero-product-stage .hero-product-image,
  html .hero .hero-product-stage .hero-model-frame,
  html .hero .hero-product-stage img,
  html .hero .hero-product-stage video {
    animation: none !important;
    transform: none !important;
    scale: none !important;
    transition: opacity 220ms ease !important;
  }

  /* The plate itself was cross-fading over 520ms on top of the layer fade,
     which is two fades stacked and reads as the card resolving rather than
     the content changing. One fade, on the content. */
  html .hero .hero-scene .hero-product-stage {
    animation: none !important;
    transition: opacity 220ms ease !important;
  }
}
