/* Viewer chrome. The screen UI brings its own styles from ../screen/styles.css. */

:root { --ink: #e7edf4; --dim: #8b98a6; }

/* Matches VOID_COLOR in viewer.js exactly — same near-black, one source of truth instead
   of two independently-hardcoded near-identical values. This is what's visible during the
   pre-ready load flash, before the canvas paints its own scene.background over it. */
/* overflow lives on html ONLY, never on body.
   html's overflow propagates to the viewport, so this still stops the page scrolling — but
   putting it on body too would make body a grouping element, and body is an ancestor of the
   CSS3D subtree. See the note at #css3d. */
html { height: 100%; margin: 0; overflow: hidden; background: #07060a; }
body {
  height: 100%; margin: 0; background: #07060a;
  font-family: "Neue Montreal", system-ui, sans-serif;
  color: var(--ink);
}
/* The fade is on the SCENE layers, not on <body>. It used to be on body, which meant #hud —
   the loader's own status line, including "could not load the model" — was composited at
   zero alpha for the entire load and then display:none'd at the exact moment it would have
   become visible. Every load looked like a black screen, and every FAILED load looked like
   a black screen forever.

   #css3d is deliberately NOT in this list: opacity is a grouping property and would flatten
   the 3D subtree it contains. It needs no fade of its own — the touchscreen inside it is
   already faded by angle, on the holder, which is a leaf and safe to composite. */
#stage, #bar, #tip { opacity: 0; transition: opacity .5s ease; }
body.ready #stage,
body.ready #bar,
body.ready #tip { opacity: 1; }

/* layer order, back to front: html/body background → CSS3D screen → WebGL canvas.
   The canvas is transparent where the LCD punches through, so the DOM shows. */
#css3d, #stage { position: fixed; inset: 0; }
/* the DOM screen sits ABOVE the canvas. colorWrite:false does not clear canvas alpha,
   so a true punch-through is unreliable through the post stack; instead the screen is
   drawn on top and faded out by angle once it turns away from the viewer. */
#stage  { z-index: 1; }
#css3d  { z-index: 5; }
#css3d { pointer-events: none; }
#css3d > div { pointer-events: none; }
/* NOTHING in #css3d's ancestor chain — AND NOTHING ON THE CSS3D-TRANSFORMED ELEMENT
 * ITSELF (.lcd-holder, below) — may carry a grouping property.
 *
 * CSS3DRenderer positions the touchscreen by building a perspective projection out of nested
 * 3D transforms. Per the CSS Transforms spec, a set of "grouping properties" force
 * transform-style to flatten on the element that has them — opacity below 1, filter, overflow
 * other than visible, clip-path, mask, mix-blend-mode, contain:paint, and will-change of any
 * of those. Flatten anything above the subtree and the projection collapses: the screen draws
 * unprojected, offset down-right and scaled up, landing across the lower half of the machine
 * instead of inside its bezel.
 *
 * Safari enforces the flattening; Chromium largely ignores it. That is why this reproduces on
 * a real iPhone and never in desktop or emulated-mobile testing, and it is why the chain is
 * now kept clear by rule rather than fixed one property at a time:
 *
 *   html         overflow:hidden, which propagates to the viewport (allowed, and the reason
 *                body no longer needs its own)
 *   body         no overflow, no opacity
 *   #css3d       no opacity fade, no overflow, no flex centring
 *   .lcd-holder  the CSS3DObject's own element — matrix3d is written directly onto this node,
 *                so a grouping property here needs no ancestor at all to flatten it. This one
 *                had overflow:hidden from the very first commit and slipped past two rounds of
 *                ancestor-chain auditing for exactly that reason: it isn't an ancestor, it's
 *                the leaf. Any clipping/rounding this element needs belongs on .screen (which
 *                already clips its 1280x800 content) instead.
 *
 * The canvas keeps its own overflow — #stage contains no 3D subtree, so it is unaffected.
 */
#stage { overflow: hidden; }
#css3d { transform-style: preserve-3d; }
#stage canvas { display: block; cursor: grab; }
#stage canvas:active { cursor: grabbing; }
/* set by the raycaster as a class rather than an inline style — an inline cursor outranks
   every selector here and would stop :active from ever showing the closed hand */
#stage canvas.over-hotspot { cursor: pointer; }

/* the LCD itself is the only DOM that takes input */
.lcd-holder { pointer-events: auto; }
.lcd-holder .screen { transform: none !important; }

#bar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 10;
  display: flex; align-items: center; gap: 18px;
  /* wraps rather than clips: the wordmark is a fixed-width image and the language control
     added another item, which together overrun a phone's width. A second row is legible;
     a control sliced off the right edge is not. */
  flex-wrap: wrap; row-gap: 10px;
  padding: 16px 22px;
  pointer-events: none;
}
/* The real gold script wordmark, not type. There is no font that reproduces it — it's a
   custom lettermark with its own underswoosh and sparkle, so it ships as an alpha PNG. */
#bar .mark {
  height: 26px; width: auto; display: block;
  /* the source art is a light-on-dark gold gradient and already carries its own contrast;
     nothing else needed, but keep it from being dragged out of the page as an image */
  -webkit-user-drag: none; user-select: none;
}
#bar .dims {
  font-size: 13px; color: var(--dim);
  border-left: 1px solid #2a333e; padding-left: 18px;
  font-variant-numeric: tabular-nums;
}
/* Sound. Off by default, so the resting state must read unambiguously as OFF at a glance —
   hence the cross, rather than a lit/unlit speaker whose state is only legible in comparison
   to a version you cannot see. */
#sound {
  margin-left: auto; pointer-events: auto; cursor: pointer;
  width: 34px; height: 32px; display: grid; place-items: center;
  padding: 0; border: 1px solid #262f3a; border-radius: 8px;
  background: rgba(22,28,36,.72); color: var(--dim);
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
#sound svg { width: 17px; height: 17px; display: block; }
#sound:hover { color: var(--ink); border-color: #37424f; }
#sound:focus-visible { outline: 2px solid #d8b06a; outline-offset: 2px; }
#sound .waves { display: none; }
#sound[aria-pressed="true"] { background: #d8b06a; border-color: #d8b06a; color: #0b0e12; }
#sound[aria-pressed="true"] .waves { display: block; }
#sound[aria-pressed="true"] .muted { display: none; }

/* Language. A two-segment control rather than a single toggle, because a lone button labelled
   "ES" is ambiguous — it reads equally as "you are in Spanish" and "switch to Spanish". Two
   segments with one lit states the current language and the alternative at once. */
#lang {
  display: flex; pointer-events: auto;
  border: 1px solid #262f3a; border-radius: 8px; overflow: hidden;
}
#lang button {
  font: inherit; font-size: 11.5px; letter-spacing: .04em; cursor: pointer;
  padding: 7px 10px; border: 0; background: rgba(22,28,36,.72); color: var(--dim);
  transition: color .15s ease, background .15s ease;
}
#lang button + button { border-left: 1px solid #262f3a; }
#lang button:hover { color: var(--ink); }
#lang button.on { background: #d8b06a; color: #0b0e12; }
#lang button:focus-visible { outline: 2px solid #d8b06a; outline-offset: -2px; }

/* nav no longer takes the auto margin — #lang does, and nav sits beside it */
#bar nav { display: flex; gap: 8px; pointer-events: auto; }
#bar nav button {
  font: inherit; font-size: 13.5px; cursor: pointer;
  color: var(--dim); background: rgba(22,28,36,.72);
  border: 1px solid #262f3a; border-radius: 8px; padding: 8px 14px;
  backdrop-filter: blur(8px);
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
#bar nav button:hover { color: var(--ink); border-color: #37424f; }
/* the browser default ring is invisible against a dark blurred pill */
#bar nav button:focus-visible { outline: 2px solid #d8b06a; outline-offset: 2px; }
#bar nav button.on { color: #0b0e12; background: #d8b06a; border-color: #d8b06a; }

/* How to use the machine.
 *
 * These are the machine's OWN four steps — the same ones silkscreened on the fascia in the
 * render — so the guide and the product never disagree. The active step is driven by the
 * real state machine in screen/spritzup-ui.js, not by a timer or a scripted tour: it moves
 * because the visitor actually paid, actually armed a slot. That also replaces a 137-character
 * run-on hint line, which was the only thing on the page telling anyone the screen was even
 * interactive and was set in the lowest-contrast text in the one place eyes never go. */
#tip {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 10;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 16px 22px 20px;
  color: var(--dim); pointer-events: none;
}
#steps {
  display: flex; align-items: center; gap: 0;
  list-style: none; margin: 0; padding: 0;
  max-width: 100%; flex-wrap: wrap; justify-content: center;
}
#steps li { display: flex; }
/* Each step is a real button: it is the control that reveals its own arrow, so it has to be
   hoverable, tappable and keyboard-reachable rather than decorative text. */
#steps button {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 16px; margin: 0;
  font: inherit; font-size: 13px; line-height: 1.25;
  background: none; border: 0; border-radius: 8px;
  color: #6f7681; cursor: pointer;
  transition: color .2s ease;
  pointer-events: auto;   /* #tip itself is click-through */
}
#steps button:hover, #steps button:focus-visible { color: var(--ink); }
#steps button:focus-visible { outline: 2px solid #d8b06a; outline-offset: 1px; }
/* hairline separators between steps, not around them */
#steps li + li { border-left: 1px solid #232b35; }
#steps .n {
  flex: none;
  width: 22px; height: 22px; border-radius: 999px;
  display: grid; place-items: center;
  font-size: 11px; font-variant-numeric: tabular-nums;
  border: 1px solid #2c3540; color: #6f7681;
  transition: color .2s ease, border-color .2s ease, background .2s ease;
}
#steps button:hover .n, #steps button:focus-visible .n { border-color: #59626e; color: var(--ink); }
#steps li.on button { color: var(--ink); }
#steps li.on .n { background: #d8b06a; border-color: #d8b06a; color: #0b0e12; }
/* steps already completed stay legible but recede */
#steps li.done button { color: #8d9aa8; }
#steps li.done .n { border-color: #6b5836; color: #a98a52; }

/* The pointer.
 *
 * On demand, not always on: it appears only while a step is hovered, focused or tapped, so
 * the render is never carrying a permanent marker. White and faint rather than brand gold —
 * gold reads as a control you are meant to press, and this is an annotation pointing AT
 * something, which is a different job. The tip is anchored to a real mesh and re-projected
 * every rendered frame, so it stays on the part as the camera orbits.
 */
#arrow {
  position: fixed; z-index: 9; left: 0; top: 0;
  pointer-events: none;
  opacity: 0; visibility: hidden;
  transition: opacity .18s ease, visibility .18s;
}
#arrow.on { opacity: .82; visibility: visible; }
#arrow svg {
  position: absolute; display: block;
  width: 62px; height: 62px;
  /* shifts the graphic so the ARROW TIP — not its box — sits on the element's origin, which
     is what gets positioned onto the part. Tip is at 3,3 of a 24 grid: 3/24 * 62 = 7.75. */
  left: -7.75px; top: -7.75px;
  color: #fff;
  filter: drop-shadow(0 2px 7px rgba(0,0,0,.7));
}
/* mirrored for parts on the left of frame, so the tail always trails away from the machine
   instead of lying across it */
#arrow.flip svg { transform: scaleX(-1); transform-origin: 7.75px 7.75px; }
#hint { margin: 0; font-size: 12px; color: #5f6772; }
/* States plainly which language the hardware ships in, so an English viewer does not assume
   the machines in Spain are running the English screen they are looking at. */
#locnote { margin: 0; font-size: 11px; color: #4d545d; }

#label {
  position: fixed; z-index: 11; left: 50%; bottom: 58px; transform: translateX(-50%);
  font-size: 13.5px; color: var(--ink);
  background: rgba(14,19,25,.88); border: 1px solid #2a333e;
  border-radius: 999px; padding: 8px 16px;
  opacity: 0; transition: opacity .16s ease; pointer-events: none;
  backdrop-filter: blur(10px);
}
#label.on { opacity: 1; }

/* Loading state, in the brand rather than as a status string. */
#hud {
  position: fixed; inset: 0; z-index: 20;
  display: grid; place-items: center; align-content: center; gap: 20px;
  font-size: 13px; color: var(--dim); pointer-events: none;
  opacity: 1; transition: opacity .35s ease;
}
#hud img { height: 30px; width: auto; opacity: .92; }
#hud .track {
  width: 168px; height: 2px; border-radius: 2px;
  background: rgba(216,176,106,.16); overflow: hidden;
}
#hud .track i {
  display: block; height: 100%; width: 0%;
  background: #d8b06a; transition: width .3s ease;
}
#hud .msg { letter-spacing: .01em; min-height: 1em; }
#hud.failed .track { display: none; }
#hud.failed .msg { color: #e0855f; max-width: 34ch; text-align: center; text-wrap: balance; }
/* faded out, not display:none — a load error has to stay on screen */
body.ready #hud { opacity: 0; }

/* Screen-reader heading. The page had no heading element at all, so assistive tech
   announced it as nothing at all. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (prefers-reduced-motion: reduce) {
  #stage, #css3d, #bar, #tip, #hud, #label { transition: none; }
}

@media (max-width: 760px) {
  #bar { padding: 12px 14px; gap: 12px; }
  #bar .dims { display: none; }
  #bar nav button { padding: 7px 11px; font-size: 12.5px; }
  #tip { padding: 12px 16px 16px; gap: 8px; }
  #steps button { font-size: 12px; padding: 5px 10px; }
  /* the separators read as clutter once the rail wraps to two rows */
  #steps li + li { border-left: 0; }
  #hint { font-size: 11px; }
}
@media (max-width: 460px) {
  /* Only the step you are ON keeps its label. Four labels wrap into a wall of text at this
     width, but bare numbers say nothing — this way the rail still tells you what to do now
     and the other three read as progress. Tapping any number still reveals its arrow. */
  #steps .t { display: none; }
  #steps li.on .t { display: inline; }
  #steps button { padding: 6px 7px; }
  #steps li.on button { padding-right: 11px; }
  /* the arrow is proportionally huge on a phone at desktop size */
  #arrow svg { width: 50px; height: 50px; left: -6.25px; top: -6.25px; }
}
/* The wordmark is an image now, so it holds a fixed width that type used to give back under
   pressure — at phone widths the logo plus four buttons overran the bar and clipped the last
   one off the right edge. Both sides give a little rather than hiding a control. */
@media (max-width: 460px) {
  #bar { padding: 10px 12px; gap: 10px; }
  #bar .mark { height: 19px; }
  #bar nav { gap: 6px; }
  #bar nav button { padding: 6px 9px; font-size: 11.5px; border-radius: 7px; }
}
