/* Unity-faithful category players — shared presentation.
   Paradigm (accepted K-Force 47 TIR STT / K-Force 55 TIR): white canvas → the
   exact Unity housing, largest object, centred at the top → the authentic
   controller centred below → compact black 6:1 Unity option cards.

   Layout is driven by the simulated frame `#player-frame[data-view]`
   (Desktop 1000×800, Portrait 390×844, Landscape 844×390) set by
   src/layout/review-tools.js, not by media queries, so every mode can be
   reviewed on any screen. Slots are percentages of the frame (a size
   container); the housing and controller are "fit in parent" inside their
   slot: width = min(slot width, slot height × art aspect), so art never
   distorts and emitter/key positions (fractions of the art box) stay exact.

   Every housing shape and controller family uses one frame contract: the
   product and its complete optical paint occupy the upper 40%; controls and
   option menus occupy the lower 60%. Source aspect ratio still decides whether
   width or height limits the art, so nothing is stretched or distorted.

   Clean mode: no chrome inside the frame. `html[data-debug="true"]` adds
   review-only hitboxes and emitter labels; `?clean=1` removes the review nav. */

:root {
  color-scheme: light;
  --canvas: #ffffff;
  --text: #16191c;
  --menu-bg: #050505;
  --menu-ink: #ffffff;
  --focus: #1f8fff;
  --glow-cyan: rgb(0 128 255);
  --menu-control-height: clamp(28px, 4.25cqh, 34px);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-synthesis: none;
}

* { box-sizing: border-box; }

html { min-width: 320px; overflow-x: hidden; background: var(--canvas); }
body { min-height: 100vh; min-height: 100dvh; margin: 0; color: var(--text); font-size: 13px; background: var(--canvas); }
button, input { font: inherit; }
button { color: inherit; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  white-space: nowrap; border: 0; clip: rect(0, 0, 0, 0); clip-path: inset(50%);
}
.skip-link { position: absolute; z-index: 300; top: -100px; left: 12px; padding: 10px 15px; color: #fff; border-radius: 10px; background: #111; }
.skip-link:focus { top: 12px; }

/* Review chrome (outside the player; absent with ?clean=1) ---------------- */
.review-tools {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center; justify-content: center;
  padding: 7px 10px; color: #5c636b; font-size: 12px; line-height: 1;
}
.review-link { color: #1a5fb4; text-decoration: none; margin-right: 6px; }
.review-button { padding: 5px 10px; cursor: pointer; color: #333; border: 1px solid #d5d8dc; border-radius: 999px; background: #fff; }
.review-button[aria-pressed="true"] { color: #fff; border-color: #111; background: #111; }

/* Simulated frame --------------------------------------------------------- */
.player-frame {
  --frame-w: 1000; --frame-h: 800; --chrome: 0px;
  position: relative;
  width: min(calc(var(--frame-w) * 1px), 100vw, calc((100vh - var(--chrome)) * var(--frame-w) / var(--frame-h)));
  aspect-ratio: var(--frame-w) / var(--frame-h);
  margin: 0 auto;
  container-type: size;
  background: var(--canvas);
}
body:has(#review-tools) .player-frame { --chrome: 44px; }
body:has(#review-tools) .player-frame:not([data-view="desktop"]) { box-shadow: 0 0 0 1px #e1e4e8; }

.player { position: absolute; inset: 0; }

/* Stage ---------------------------------------------------------------- */
.light-stage {
  position: absolute; left: 3%; top: 2%; width: 94%; height: 38%;
  --stage-h: 38cqh;
  display: grid; place-items: center; isolation: isolate;
  /* Unity optical children are larger than the housing: keep the halo visible. */
  overflow: visible; contain: layout;
}
.stage-mount { display: grid; width: 100%; height: 100%; place-items: center; }

/* The housing content box: every emitter transform is a fraction of it. */
.bar-wrap {
  /* Conservative paint allowance for the nested 3 px + 9 px glow filters.
     Twice their combined blur radii exceeds the browser filter kernel while
     remaining source- and product-neutral. The stage fitter reads this value. */
  --optical-filter-outset: 24px;
  position: relative;
  width: min(100%, calc(var(--stage-h) * var(--housing-aspect, 4.95)));
  aspect-ratio: var(--housing-aspect, 4.95);
  overflow: visible;
  /* Outer uniform fitter (emitter-stage.js): exactly one inline
     `translate() scale()` about the housing centre keeps the complete optical
     envelope inside the frame and above the controller / cards. Nothing
     inside the wrap is resized or clipped; --fit-scale is diagnostic only. */
  --fit-scale: 1;
  transform-origin: 50% 50%;
  transform-box: border-box;
  will-change: transform;
}
.bar-wrap > img.housing-art { position: relative; z-index: 2; display: block; width: 100%; height: 100%; user-select: none; pointer-events: none; }
.emitters { position: absolute; z-index: 3; inset: 0; pointer-events: none; overflow: visible; }

/* Unity LED Module hierarchy rendered as nested transforms (emitter-stage.js):
   .emitter (root RectTransform box + pivot + rotation, inline)
     > .emitter-glow (colorized child anchors/pivot + rotation/scale, inline)
         > .emitter-glow-fill (painted glowLED.png mask, colour + alpha)
         > .emitter-core (white glow.png child anchors/pivot + scale, inline) */
.emitter { --on: 0; --rgb: 255 191 0; --color-alpha: 1; --core-alpha: 1; position: absolute; overflow: visible; }

/* Half Octo source hierarchy: Unity paints the two edge heads on its Outer
   canvas, then the two centre heads on the later Center canvas. The stage now
   renders its root-order-sorted copy; these tiers document and preserve that
   parent-canvas paint relationship without changing manager order. */
html[data-product-id="half-octo"] .emitter[data-module="left-1"],
html[data-product-id="half-octo"] .emitter[data-module="right-4"] { z-index: 0; }
html[data-product-id="half-octo"] .emitter[data-module="left-2"],
html[data-product-id="half-octo"] .emitter[data-module="right-3"] { z-index: 1; }
.emitter-glow, .emitter-glow-fill, .emitter-core { position: absolute; display: block; pointer-events: none; }
.emitter-glow { overflow: visible; }
.emitter-glow-fill {
  z-index: 1; inset: 0;
  opacity: calc(var(--on) * var(--color-alpha));
  background: rgb(var(--rgb) / .66);
  -webkit-mask: url("assets/glow/glow-led.png") center / 100% 100% no-repeat;
  mask: url("assets/glow/glow-led.png") center / 100% 100% no-repeat;
  filter: saturate(1.2) brightness(1.08) drop-shadow(0 0 3px rgb(var(--rgb) / .7)) drop-shadow(0 0 9px rgb(var(--rgb) / .45));
  mix-blend-mode: screen;
  transition: opacity 8ms linear;
}
/* Twin-colour layers (LEDModuleSpecialTwinColor.DisplayHalfGlow): the GSM
   hover preview swaps the colorized glow sprite to the copied Unity half
   masks (GlowLED_TopHalf.png / GlowLED_BottomHalf.png) and restores the full
   glowLED.png on exit. Only the mask image changes; geometry is untouched. */
.emitter[data-half-glow="top"] .emitter-glow-fill {
  -webkit-mask-image: url("assets/glow/glow-led-top-half.png");
  mask-image: url("assets/glow/glow-led-top-half.png");
}
.emitter[data-half-glow="bottom"] .emitter-glow-fill {
  -webkit-mask-image: url("assets/glow/glow-led-bottom-half.png");
  mask-image: url("assets/glow/glow-led-bottom-half.png");
}
/* Twin-colour interactive PRIMARY layer (LEDModuleSpecialTwinColor.Awake:
   IsInteractive = IsPrimaryLED): one transparent activation surface over the
   co-located head, above the pointer-events-none emitter layer. Only twin
   products render it (emitter-stage.js); paint is untouched. */
.twin-primary-hit {
  position: absolute; z-index: 4; padding: 0; margin: 0; border: 0;
  background: transparent; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.twin-primary-hit:focus-visible { outline: 2px solid #57a4ff; outline-offset: 2px; }
.emitter-core {
  z-index: 2;
  opacity: calc(var(--on) * var(--core-alpha));
  background: rgb(255 255 255 / .82);
  -webkit-mask: url("assets/glow/glow-core.png") center / 100% 100% no-repeat;
  mask: url("assets/glow/glow-core.png") center / 100% 100% no-repeat;
  filter: brightness(1.2) drop-shadow(0 0 3px rgba(255, 255, 255, .7));
  mix-blend-mode: screen;
  transition: opacity 8ms linear;
}
.emitter-label { display: none; }

/* Controller ------------------------------------------------------------ */
.controller-section {
  position: absolute; left: 0; top: 41%; width: 100%; height: 41%;
  --controller-h: 41cqh;
  display: grid; place-items: center;
}
.controller-wrap {
  --controller-aspect: 1.45;
  position: relative;
  width: min(100%, calc(var(--controller-h) * var(--controller-aspect)));
  touch-action: manipulation;
  filter: drop-shadow(0 14px 18px rgba(0, 0, 0, .28));
}
.controller-base, .controller-overlay { position: absolute; inset: 0; display: block; width: 100%; height: 100%; pointer-events: none; user-select: none; }
.controller-base { position: relative; z-index: 1; }
.controller-overlay { z-index: 2; }
.controller-overlay[hidden] { display: none; }

/* Night backlight: one masked red wash per printed key. */
.night-backlight { position: absolute; z-index: 3; inset: 0; opacity: 0; pointer-events: none; mix-blend-mode: screen; transition: opacity .18s; }
.night-key {
  position: absolute; display: block; background: rgba(255, 24, 0, .5);
  -webkit-mask: url("assets/glow/glow-button.png") center / 100% 100% no-repeat;
  mask: url("assets/glow/glow-button.png") center / 100% 100% no-repeat;
  filter: blur(1px);
}
.controller-wrap[data-night="true"] .night-backlight { opacity: 1; }

/* Face buttons: the <button> is the Unity ControlBoxButton rect; .key-glow is
   the painted feedback. Sprite glow (Direct / Grand) is masked by glowBtn.png;
   rect glow (Abstract) is Unity's translucent cyan rectangle; slider zones
   paint nothing (activation images do the work). */
.cb-button {
  position: absolute; z-index: 7; padding: 0; margin: 0; border: 0; background: transparent; cursor: pointer;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
.cb-button:disabled { cursor: not-allowed; }
.cb-button.slider-zone { pointer-events: none; }
.key-glow { position: absolute; inset: 0; display: block; opacity: 0; pointer-events: none; transition: opacity 90ms ease, background-color 90ms ease; }
.glow-sprite .key-glow {
  background: #ffffff;
  -webkit-mask: var(--key-glow-mask, none) center / 100% 100% no-repeat;
  mask: var(--key-glow-mask, none) center / 100% 100% no-repeat;
}
.glow-sprite:active .key-glow { opacity: .42; }
.glow-sprite[data-glow="true"] .key-glow { opacity: .78; background: #38c2ff; }
.glow-sprite[data-control="night"][data-glow="true"] .key-glow { background: #ff3a1e; }
.glow-sprite:disabled .key-glow { opacity: 0; }
.glow-none .key-glow { display: none; }
.cb-button:focus-visible { outline: none; }
.cb-button:focus-visible::after {
  content: ""; position: absolute; inset: 0; border-radius: 12%; pointer-events: none;
  box-shadow: 0 0 0 2px var(--focus), 0 0 0 4px rgba(255, 255, 255, .85);
}

/* Multi-position slider (Cig Plug rocker, Direct Control speed, Supreme
   Off / Rear / Front / All power slider): an invisible role="slider" track over
   the printed control. Drag or arrow keys; the printed art changes through the
   full-plate activation overlays, exactly like the Unity ActivatedImages. */
.cb-slider { position: absolute; z-index: 8; cursor: grab; touch-action: none; border-radius: 8px; background: transparent; }
.cb-slider-vertical { cursor: ns-resize; }
.cb-slider-horizontal { cursor: ew-resize; }
.cb-slider:active { cursor: grabbing; }
.cb-slider:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; background: rgba(31, 143, 255, .08); }

/* Abstract Controller (hard-wire cable functions): 6.2:1 strip of 2:1 button
   plates placed by FitInParent pivots, plus the "*HARDWIRE CABLE FUNCTIONS" note.
   This is a functional legend rather than a physical control box. Its desktop
   cap therefore favors legibility while preserving the same scale everywhere. */
.faceplate-abstract { --controller-aspect: 4.2; width: min(68%, calc(var(--controller-h) * var(--controller-aspect))); filter: none; }
.player-frame[data-view="portrait"] .faceplate-abstract, .player-frame[data-view="landscape"] .faceplate-abstract { width: min(100%, calc(var(--controller-h) * var(--controller-aspect))); }
.abstract-strip { position: relative; width: 100%; aspect-ratio: 6.2; }
.abstract-button { top: 0; height: 100%; overflow: hidden; border-radius: 3%; }
/* Unity layering: the cyan ControlBoxButton image is child 0, inset to the
   inner face (anchors .05–.95 × .1–.9); the printed art (transparent interior,
   black border) is child 1 stretched over it. So the glow sits behind the art. */
.abstract-button-art { position: relative; z-index: 1; display: block; width: 100%; height: 100%; object-fit: fill; pointer-events: none; user-select: none; }
.glow-rect .key-glow { z-index: 0; inset: 10% 5%; background: var(--glow-cyan); }
.glow-rect[data-glow="true"] .key-glow { opacity: .8; }
.glow-rect:disabled .key-glow { opacity: 0; }
.abstract-note { display: block; width: 58%; margin: 2.5% auto 0; user-select: none; pointer-events: none; }

/* Option menus (Unity SELECT COLORS / SELECT UPGRADES) -------------------- */
.options-section { position: absolute; left: 0; top: 88%; width: 100%; height: 10%; min-width: 0; overflow: visible; }
.options-section[hidden] { display: none; }
.config-dock { display: grid; grid-template-columns: repeat(2, 49.5%); grid-template-rows: 100%; justify-content: space-between; align-items: center; width: 100%; height: 100%; }
.config-dock[data-card-count="1"] { grid-template-columns: 49.5%; justify-content: center; }
.config-card[data-card="colors"] { order: 1; }
.config-card[data-card="upgrades"] { order: 2; }

/* Every Unity menu root is a 6:1 black plate. */
.config-card {
  position: relative; isolation: isolate; justify-self: center;
  width: auto; height: 100%; max-width: 100%; aspect-ratio: 6 / 1;
  margin: 0; padding: 0; min-width: 0; min-height: 0; overflow: visible;
  color: var(--menu-ink); border: 0; background: transparent;
}
.config-card::before {
  content: ""; position: absolute; z-index: -1; inset: 0; background: var(--menu-bg);
  clip-path: polygon(7px 0, calc(100% - 7px) 0, 100% 7px, 100% calc(100% - 7px), calc(100% - 7px) 100%, 7px 100%, 0 calc(100% - 7px), 0 7px);
}
/* Unity prefab coordinates: content x 5–95 %; title y 10–30 %; body y 44–86 %. */
.config-title {
  position: absolute; z-index: 1; top: 10%; left: 5%; width: 90%; height: 20%; margin: 0; padding: 0; overflow: hidden;
  color: transparent; font-size: 0; white-space: nowrap;
  background: var(--unity-title-art, none) center / contain no-repeat;
}
.config-body { position: absolute; z-index: 1; top: 44%; left: 5%; display: flex; gap: 2%; align-items: center; justify-content: center; width: 90%; height: 42%; min-width: 0; overflow: visible; }
.config-body > * { min-width: 0; }
.colors-card .config-body > * { flex: 0 0 49%; width: 49%; }
.colors-card.colors-only .config-body > .color-pair { flex: 0 0 60%; width: 60%; }
.upgrades-card .upgrade-stack { flex: 0 0 60%; width: 60%; }
/* Upgrades Menu (Dual Color TD & Alley + MultiColor TA): two equal 7.5:1 plates
   side by side (Unity sub-panel anchors x 0–.49 and .51–1), same size as the
   colour pair, so the D and M cards read as one row. */
.upgrades-card[data-upgrade-count="2"] .upgrade-stack { flex: 0 0 100%; width: 100%; gap: 2%; }
.upgrades-card[data-upgrade-count="2"] .upgrade-stack > .unity-art-button { flex: 0 0 49%; width: 49%; }
.option-stack, .upgrade-stack { display: flex; align-items: center; justify-content: center; min-width: 0; height: var(--menu-control-height); }

.option-button, .cycle-button {
  display: inline-flex; align-items: center; justify-content: center; min-height: var(--menu-control-height);
  padding: 0; cursor: pointer; color: transparent; border: 0; border-radius: 0; background: transparent;
  -webkit-tap-highlight-color: transparent;
}
.option-button:disabled, .cycle-button:disabled { opacity: .42; cursor: not-allowed; }
.option-text { display: block; min-width: 0; }

/* Authentic 600×80 state sprites (7.5:1) inside larger transparent hit rects. */
.unity-art-button { position: relative; width: 100%; height: var(--menu-control-height); overflow: visible; }
.unity-art-button::before {
  content: ""; position: absolute; z-index: 0; top: 50%; left: 0; width: 100%; aspect-ratio: 7.5 / 1; transform: translateY(-50%); pointer-events: none;
  background: var(--unity-art-unselected) center / 100% 100% no-repeat;
}
.unity-art-button[aria-pressed="true"]::before { background-image: var(--unity-art-selected); }

/* Dual swatch face: the buttons cover each half; visible colours sit in the
   window drawn by colors_color btn border.png. Colour is the label. */
.color-pair { position: relative; display: flex; container-type: inline-size; align-items: center; height: var(--menu-control-height); overflow: visible; }
.color-pair::after {
  content: ""; position: absolute; z-index: 2; top: 50%; left: 0; width: 100%; aspect-ratio: 7.5 / 1; transform: translateY(-50%); pointer-events: none;
  background: var(--color-pair-border) center / 100% 100% no-repeat;
}
.cycle-button.swatch-only { position: relative; flex: 0 0 50%; width: 50%; height: var(--menu-control-height); }
.cycle-button.swatch-only .swatch {
  position: absolute; z-index: 1; top: 50%; display: block; width: 73.333%;
  height: min(85%, calc(100cqw / 7.5 * .85)); background: var(--swatch, #6b737c); transform: translateY(-50%); transition: filter 90ms ease;
}
#left-color .swatch { right: 0; border-radius: 3px 0 0 3px; }
#right-color .swatch { left: 0; border-radius: 0 3px 3px 0; }
/* Colors Menu (Color Single): the same plate slot with the undivided
   colors_single color btn border.png window (set through the shared
   --color-pair-border variable) and one full-width picker whose swatch spans
   the whole window — the two half swatches would otherwise leave the divider
   gap the single border does not print. */
.color-pair.color-single > .cycle-button.swatch-only { flex: 0 0 100%; width: 100%; }
#single-color .swatch { left: 50%; width: 73.333%; border-radius: 3px; transform: translate(-50%, -50%); }
.cycle-button.swatch-only:focus-visible { outline-offset: -2px; }

/* Colors Menu (Color Double + Tri Color Modifier): Unity layers its Double
   Color Pick Button Set and Tri Color Pick Button Set at the identical left
   half anchors; the ENABLE TRI COLOR plate owns the full right half. Only one
   left selector is painted/focusable at a time. The tri picker uses the
   undivided border plus the source Quad Color Pick Image tinted by the second
   secondary colour. In tri mode the old dual-picker arrow overlay is removed.
   The source wedge includes obsolete arrow art in its outer 80 px gutters, so
   its central 440 px window is cropped and expanded across the retained colour
   window instead of exposing those four markers. */
.tri-selector-slot {
  position: relative; flex: 0 0 49%; width: 49%; height: var(--menu-control-height);
  container-type: inline-size; overflow: visible;
}
.tri-selector-slot > .color-pair,
.tri-selector-slot > .tri-picker { position: absolute; inset: 0; width: 100%; height: 100%; }
.tri-selector-slot[data-mode="tri"] > .color-pair { pointer-events: none; }
.tri-selector-slot[data-mode="tri"] > .color-pair::after { display: none; }
.tri-selector-slot[data-mode="tri"] > .tri-picker { z-index: 3; }
.tri-selector-slot > [hidden] { display: none; }
.cycle-button.tri-picker {
  container-type: inline-size; overflow: visible;
}
.cycle-button.tri-picker::after {
  content: ""; position: absolute; z-index: 2; top: 50%; left: 0; width: 100%; aspect-ratio: 7.5 / 1; transform: translateY(-50%); pointer-events: none;
  background: var(--tri-picker-border) center / 100% 100% no-repeat;
}
.cycle-button.tri-picker .swatch {
  position: absolute; z-index: 1; top: 50%; left: 50%; display: block; width: 73.333%;
  height: min(85%, calc(100cqw / 7.5 * .85)); background: var(--swatch, #6b737c);
  -webkit-mask: var(--tri-wedge-plate) center / 136.364% 100% no-repeat;
  mask: var(--tri-wedge-plate) center / 136.364% 100% no-repeat;
  transform: translate(-50%, -50%); transition: filter 90ms ease;
}
.cycle-button.tri-picker .quad-swatch {
  position: absolute; z-index: 1; top: 50%; left: 50%; display: block; width: 73.333%;
  height: min(85%, calc(100cqw / 7.5 * .85)); transform: translate(-50%, -50%);
  background: var(--swatch, #6b737c);
  -webkit-mask: var(--quad-plate) center / 136.364% 100% no-repeat;
  mask: var(--quad-plate) center / 136.364% 100% no-repeat;
  transition: filter 90ms ease;
}
.cycle-button.tri-picker:focus-visible { outline-offset: -2px; }

/* Product stage slots ------------------------------------------------------
   The shared 2–40% stage is the complete product band for every housing and
   controller family. Menuless products may use the full lower 60%. */
html[data-menuless="true"] .controller-section { top: 41%; height: 57%; --controller-h: 41cqh; }

/* Portrait (390×844): bar, controller, then cards stacked full width ------- */
.player-frame[data-view="portrait"] .light-stage { left: 2%; top: 2%; width: 96%; height: 38%; --stage-h: 38cqh; }
.player-frame[data-view="portrait"] .controller-section { left: 3%; top: 41%; width: 94%; height: 28%; --controller-h: 28cqh; }
.player-frame[data-view="portrait"] .options-section { left: 1%; top: 71%; width: 98%; height: 26%; }
.player-frame[data-view="portrait"] .config-dock { grid-template-columns: 100%; grid-template-rows: repeat(2, 47%); justify-content: normal; align-content: end; align-items: end; gap: 1cqh; }
.player-frame[data-view="portrait"] .config-dock[data-card-count="1"] { grid-template-rows: 47%; }
/* Row height is 47 % of the 26 % options slot ≈ 12.2 cqh; a 6:1 plate is then ≤ 73 cqh wide. */
.player-frame[data-view="portrait"] .config-card { width: min(100%, 73cqh); height: auto; }
/* Touch views: every enabled option button is a ≥ 44×44 px transparent target;
   the 7.5:1 sprite art / swatch window inside stays sized by width, unchanged. */
.player-frame[data-view="portrait"] { --menu-control-height: 44px; }
.player-frame[data-view="portrait"] .config-dock button,
.player-frame[data-view="landscape"] .config-dock button { min-width: 44px; min-height: 44px; }
html[data-menuless="true"] .player-frame[data-view="portrait"] .controller-section { top: 41%; height: 57%; --controller-h: 28cqh; }

/* Landscape (844×390): bar across the top, cards lower-left, controller lower-right */
.player-frame[data-view="landscape"] { --menu-control-height: 44px; }
.player-frame[data-view="landscape"] .light-stage { left: 5%; top: 2%; width: 90%; height: 38%; --stage-h: 38cqh; }
.player-frame[data-view="landscape"] .controller-section { left: 41%; top: 41%; width: 57%; height: 57%; --controller-h: 57cqh; }
.player-frame[data-view="landscape"] .options-section { left: 2%; top: 46%; width: 38%; height: 46%; }
.player-frame[data-view="landscape"] .config-dock { grid-template-columns: 100%; grid-template-rows: repeat(2, 46%); justify-content: normal; align-content: end; align-items: end; gap: 2cqh; }
.player-frame[data-view="landscape"] .config-dock[data-card-count="1"] { grid-template-rows: 46%; }
.player-frame[data-view="landscape"] .config-card { width: 100%; height: auto; }
.player-frame[data-view="landscape"] .controller-wrap { filter: drop-shadow(0 8px 10px rgba(0, 0, 0, .24)); }
html[data-menuless="true"] .player-frame[data-view="landscape"] .controller-section { left: 5%; top: 41%; width: 90%; height: 57%; --controller-h: 57cqh; }

/* The hardwire legend is short rather than a physical box. Align it to the
   bottom of the shared controller band so the lower 60% reads as a deliberate
   controller → menu stack without introducing a product-specific art slot. */
html[data-controller="abstract"] .player-frame[data-view="portrait"] .controller-section { align-items: end; }

/* Developer overlay (review only; absent in clean mode) -------------------- */
html[data-debug="true"] .bar-wrap { outline: 1px dotted rgba(0, 0, 0, .45); }
html[data-debug="true"] .emitter { outline: 1px dashed rgba(255, 0, 140, .9); outline-offset: -1px; }
html[data-debug="true"] .emitter-glow { outline: 1px dotted rgba(0, 160, 255, .8); }
html[data-debug="true"] .emitter-core { outline: 1px dotted rgba(255, 255, 255, .9); }
html[data-debug="true"] .emitter-label {
  position: absolute; z-index: 9; left: 50%; top: 50%; display: block; padding: 0 3px; transform: translate(-50%, -50%);
  color: #fff; font: 700 10px/1.4 ui-monospace, Menlo, monospace; background: rgba(200, 0, 110, .9); border-radius: 3px; pointer-events: none;
}
html[data-debug="true"] .cb-button, html[data-debug="true"] .abstract-button { outline: 1px solid rgba(0, 140, 255, .85); outline-offset: -1px; background-color: rgba(0, 140, 255, .1); }
html[data-debug="true"] .cb-slider { outline: 1px solid rgba(255, 140, 0, .9); background: rgba(255, 140, 0, .1); }
html[data-debug="true"] .config-card { outline: 1px dashed rgba(0, 140, 255, .8); }

/* Hover is a mouse affordance, never a touch state. The capability query
   excludes touch-only devices; data-pointer-modality also suppresses sticky
   :hover on hybrid touch + mouse hardware until the mouse moves again. */
@media (hover: hover) and (pointer: fine) {
  html[data-pointer-modality="mouse"] .review-link:hover { text-decoration: underline; }
  html[data-pointer-modality="mouse"] .glow-sprite:hover .key-glow { opacity: .22; }
  html[data-pointer-modality="mouse"] .glow-sprite[data-glow="true"]:hover .key-glow { opacity: .9; }
  html[data-pointer-modality="mouse"] .glow-rect:hover .key-glow { opacity: .2; }
  html[data-pointer-modality="mouse"] .unity-art-button:hover::before { filter: brightness(1.12); }
  html[data-pointer-modality="mouse"] .cycle-button.swatch-only:hover .swatch { filter: brightness(1.18); }
  html[data-pointer-modality="mouse"] .cycle-button.tri-picker:hover:not(:disabled) .swatch,
  html[data-pointer-modality="mouse"] .cycle-button.tri-picker:hover:not(:disabled) .quad-swatch { filter: brightness(1.18); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: .001ms !important; }
}
