/* ============================================================================
   WARP ZONE v3 — Mr. Avery's ART-CHROME shell (2026-07-29 evening mock).
   Load AFTER css/windows.css.  Scoped to .win.w-arcade / .wz-* only.

   v4 (2026-07-30) — Mr. Avery's refinement pass. THREE framing systems, and
   knowing which is which is how you avoid breaking this file:

   ① TRACED    the hero ring only. An inline SVG whose polygons come from HIS
               OWN sample via _tools/warpzone/trace-hero.py. Fixed aspect
               1029/456. The well clip-path below IS the traced inner loop —
               re-run the script and paste BOTH if the shape ever changes.
               The riser (top-edge step) moved to 27% in that script; see the
               .wz-topbar and .wz-hero blocks.
   ② DRAWN     every other HOME panel — IN THE ZONE, EXPLORE ARCADE. ONE
               parametric generator in js/arcade.js (pixelFrameSVG) draws the
               same stepped-corner double line at any w×h, redrawn by a
               ResizeObserver. Colours are per-panel --pf-* props set HERE.
   ③ BORDER-IMAGE   the v2 12×12 pixel frame (--px-* props) — for sub-boxes and
               controls: cards, the zone's two inner sections, tabs bar, search
               box, row arrows, suggest box. `background-clip: padding-box`
               stays REQUIRED on every one of them or the notched corners square
               off.

   The ONLY art left in the shell is img/arcade/wz/logo.webp (the wordmark, its
   tagline baked in). img/arcade/wz/inthezone-frame.webp — the old opaque IN THE
   ZONE panel with its labels baked in — is RETIRED but kept on disk.

   ⚠ Type inside the hero and the zone is sized in cqi so it scales with the
     panel (both are container-type: inline-size).
   ⚠ FONTS: Press Start 2P and VT323 are gone OS-wide (2026-07-29). Big display
     = 'Minecraft' 700, chrome = 'Pixeloid Sans' 700, digits = 'Pixeloid Mono'.
     Never copy an old px size across — see the metric table in css/fonts.css.

   STRUCTURE
     .win-body.wz-body            8px padding = outer breathing room
       .wz-stars                  drifting starfield, z0, decorative
       #arcade-sites.wz-app       flex column, z1 — carries the outer frame
         .wz-topbar               logo art · HOME/AVERY ARCADE/category tabs ·
                                  search · SUGGEST (+ its absolute popover)
         .wz-scroll               THE single scroll container
           .wz-view[data-view=home]      hero (SVG frame) + IN THE ZONE art +
                                         EXPLORE ARCADE
           .wz-view[data-view=gamebox]   ADOPTED .gb-cabinet-wrap (the cabinet)
           .wz-view[data-view=cat:*]     one card grid per category
           .wz-view[data-view=all]       ALL GAMES grid = VIEW ALL + search hits

   ⚠ ONE scroll container on purpose: the lazy-art IntersectionObserver takes
   .wz-scroll as its root, so the root stays valid whichever tab is showing.
   (.wz-noscroll freezes it while the cabinet tab is up — the cabinet is a
   fixed viewport, not a page.)
   ⚠ SPECIFICITY: windows.css has `.w-arcade .win-body { overflow-y: auto }`
   at (0,2,0); the body rule below is (0,4,0) so it wins regardless of order.
   ⚠ Narrow layout is .wz-narrow/.wz-tiny classes from a ResizeObserver, NOT
   @media — media queries measure the viewport, these are draggable windows.
   ========================================================================= */

.win.w-arcade .win-body.wz-body {
  position: relative;
  padding: 8px;
  /* clip, not hidden: .wz-stars below is deliberately oversized (inset:-60px)
     for the drift animation, and `overflow: hidden` still creates a scroll
     container even with nothing to scroll to on purpose — a Tab/.focus()/
     scrollIntoView() on anything inside could shift the whole view up to
     ~60px with no way back. `overflow: clip` clips identically but isn't a
     scroll container. .wz-scroll (below) stays the one real scroller. */
  overflow: clip; overflow-y: clip;
  background:
    radial-gradient(rgba(138,108,255,.09) 1px, transparent 1.6px) 0 0 / 26px 26px,
    radial-gradient(120% 90% at 50% 0%, #1b1040 0%, #0d0722 55%, #07040f 100%);
  display: flex;
  flex-direction: column;

  /* the pixel-frame sources — 12×12 SVG, slice 4. Same path, six fills. */
  --px-cyan:       url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath d='M4 0h4v2H4zM2 2h2v2H2zM8 2h2v2H8zM0 4h2v4H0zM10 4h2v4h-2zM2 8h2v2H2zM8 8h2v2H8zM4 10h4v2H4z' fill='%2316E0E0'/%3E%3C/svg%3E");
  --px-cyan-dim:   url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath d='M4 0h4v2H4zM2 2h2v2H2zM8 2h2v2H8zM0 4h2v4H0zM10 4h2v4h-2zM2 8h2v2H2zM8 8h2v2H8zM4 10h4v2H4z' fill='%2316E0E0' fill-opacity='.45'/%3E%3C/svg%3E");
  --px-pink:       url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath d='M4 0h4v2H4zM2 2h2v2H2zM8 2h2v2H8zM0 4h2v4H0zM10 4h2v4h-2zM2 8h2v2H2zM8 8h2v2H8zM4 10h4v2H4z' fill='%23FF2E7E'/%3E%3C/svg%3E");
  --px-purple:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath d='M4 0h4v2H4zM2 2h2v2H2zM8 2h2v2H8zM0 4h2v4H0zM10 4h2v4h-2zM2 8h2v2H2zM8 8h2v2H8zM4 10h4v2H4z' fill='%238A6CFF'/%3E%3C/svg%3E");
  --px-purple-dim: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath d='M4 0h4v2H4zM2 2h2v2H2zM8 2h2v2H8zM0 4h2v4H0zM10 4h2v4h-2zM2 8h2v2H2zM8 8h2v2H8zM4 10h4v2H4z' fill='%238A6CFF' fill-opacity='.55'/%3E%3C/svg%3E");
  --px-gold:       url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath d='M4 0h4v2H4zM2 2h2v2H2zM8 2h2v2H8zM0 4h2v4H0zM10 4h2v4h-2zM2 8h2v2H2zM8 8h2v2H8zM4 10h4v2H4z' fill='%23FFD400'/%3E%3C/svg%3E");
  --px-dark:       url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath d='M4 0h4v2H4zM2 2h2v2H2zM8 2h2v2H8zM0 4h2v4H0zM10 4h2v4h-2zM2 8h2v2H2zM8 8h2v2H8zM4 10h4v2H4z' fill='%230a0618'/%3E%3C/svg%3E");
}

/* ── starfield ─────────────────────────────────────────────────────────────
   Tiled radial-gradient layers on one slow transform drift. No DOM nodes,
   no per-frame JS. Visible through the 8px body padding around the frame. */
.wz-stars {
  position: absolute; inset: -60px; z-index: 0; pointer-events: none; opacity: .55;
  background-image:
    radial-gradient(1.5px 1.5px at 12% 18%, #9ad9ff 99%, transparent),
    radial-gradient(1.5px 1.5px at 68% 42%, #ff9ad9 99%, transparent),
    radial-gradient(1px 1px at 34% 76%, #ffffff 99%, transparent),
    radial-gradient(1px 1px at 88% 12%, #c6f542 99%, transparent),
    radial-gradient(1.5px 1.5px at 52% 62%, #16E0E0 99%, transparent);
  background-size: 260px 220px, 340px 300px, 190px 170px, 420px 380px, 300px 260px;
  animation: wz-drift 78s linear infinite;
}
@keyframes wz-drift { to { transform: translate3d(-260px, -220px, 0); } }

/* ── the app + its OUTER pixel frame (the mock's window-wide blue border) ── */
.wz-app {
  position: relative; z-index: 1;
  flex: 1; min-height: 0;
  display: flex; flex-direction: column;
  padding: 0; margin: 0; gap: 0;
  overflow: hidden;
  border: 6px solid transparent;
  border-image: var(--px-cyan-dim) 4 stretch;
  background: rgba(8,5,18,.35);
  background-clip: padding-box;
  box-shadow: inset 0 0 0 1px rgba(22,224,224,.14), inset 0 0 34px rgba(22,224,224,.07);
  --c: #16E0E0;                    /* re-point the arcade accent to neon cyan */
  --wz-pink: #FF2E7E;
  --wz-purple: #8A6CFF;
  --wz-gold: #FFD400;
}

/* one-shot warp-in on open */
.wz-app.wz-in { animation: wz-warp .46s cubic-bezier(.2,.8,.3,1) both; }
@keyframes wz-warp {
  from { opacity: 0; transform: scale(.965); }
  to   { opacity: 1; transform: none; }
}

/* ── top bar: logo art · tabs · search ─────────────────────────────────────
   ⚠ THE LOGO BLOCK IS RISER-WIDTH (2026-07-30, Mr. Avery). In his mock the
   wordmark fills the whole top-left negative space and the first nav tab starts
   exactly at the x where the hero frame's TALL section begins. Both come from
   ONE number: --wz-riser-px, published by the ResizeObserver in js/arcade.js as
   (hero's left offset) + RISER_FRAC × (hero's width). So .wz-brand is that wide,
   .wz-nav therefore begins on the riser, and the logo is sized BY its box rather
   than by a hand-picked px height — the alignment survives every window size.
   Don't put a fixed height back on .wz-logo; --wz-logo-max is the only cap. */
.wz-topbar {
  flex: none;
  position: relative;   /* anchor for .wz-sugpop */
  display: flex; align-items: center; gap: 0;
  padding: 6px 14px 4px;
  background: linear-gradient(180deg, rgba(10,6,26,.5), rgba(10,6,26,.08));
  border-bottom: 1px solid rgba(22,224,224,.28);
  --wz-tb-pad: 14px;
  --wz-logo-max: 78px;
}
.wz-brand {
  flex: none; display: flex; align-items: center;
  /* fall back to a sane box before the observer's first tick.
     −8px = .wz-tab's own padding-left, so the first tab's TEXT (not its click
     box) lands on the riser, which is how the mock reads. */
  width: calc(var(--wz-riser-px, 260px) - var(--wz-tb-pad) - 8px);
  min-width: 150px;
  padding-right: 12px;
}
.wz-logo {
  display: block;
  width: 100%; height: auto;
  max-height: var(--wz-logo-max);
  object-fit: contain; object-position: left center;
  filter: drop-shadow(0 0 10px rgba(22,224,224,.3));
}

/* .wz-navwrap is the flex CHILD of .wz-topbar now (was .wz-nav itself) — it
   exists so the ‹› rail buttons below have an absolute-positioning anchor the
   same way .wz-rowwrap gives the content rows theirs (see that comment further
   down). .wz-nav keeps its own overflow-x scroller inside it at 100% width. */
.wz-navwrap { position: relative; flex: 1; min-width: 0; }
.wz-nav { display: flex; align-items: center; gap: 2px; width: 100%; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.wz-nav::-webkit-scrollbar { height: 0; display: none; }
/* ⚠ 2026-08-06 (audit finding, arcade/REVIEW.md): tabs measured 40px tall,
   under the 44px touch-target guideline. min-height (border-box, so it
   includes the border-bottom) + inline-flex centering grows the real hit
   box to 44px without touching font-size/padding — .wz-app.wz-tiny below
   only overrides font-size/padding, so min-height still applies there too.
   .wz-nav keeps `overflow-x:auto` + `white-space:nowrap` on the tab text,
   so this is a pure height change: it cannot cause the tab row to wrap. */
.wz-tab {
  background: none; border: none; cursor: pointer; white-space: nowrap;
  font: 700 12px 'Pixeloid Sans', monospace;   /* was 9px PS2P — cap ×1.29 */
  color: rgba(233,240,255,.62); padding: 12px 8px 9px;
  border-bottom: 4px solid transparent;
  min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  transition: color .12s, border-color .12s;
}
.wz-tab:hover { color: #fff; }
.wz-tab.wz-on { color: #fff; border-bottom-color: var(--wz-gold); text-shadow: 0 0 10px rgba(255,212,0,.6); }
.wz-tab:focus-visible { outline: 3px solid var(--c); outline-offset: 2px; }
/* the cabinet tab reads as its own app — pink, like its old window chrome */
.wz-tab-gb { color: rgba(255,46,126,.85); }
.wz-tab-gb:hover { color: #ff7eb0; }
.wz-tab-gb.wz-on { color: var(--wz-pink); text-shadow: 0 0 10px rgba(255,46,126,.6); }

.wz-search {
  flex: none; display: flex; align-items: center; gap: 7px;
  padding: 7px 10px;
  background: rgba(12,7,30,.85);
  background-clip: padding-box;
  border: 4px solid transparent;
  border-image: var(--px-cyan-dim) 4 stretch;
  transition: box-shadow .12s;
}
.wz-search:focus-within {
  border-image-source: var(--px-cyan);
  box-shadow: 0 0 14px rgba(22,224,224,.4);
}
.wz-search-ico { font-size: 13px; line-height: 1; filter: drop-shadow(0 0 4px var(--c)); }
.wz-search input {
  background: none; border: none; outline: none;
  width: 150px;
  font: 700 11px 'JetBrains Mono', monospace; letter-spacing: .04em;
  color: #fff; caret-color: var(--c);
}
.wz-search input::placeholder { color: rgba(154,217,255,.55); }

/* ── SUGGEST A GAME — top-bar trigger + popover (moved here 2026-07-30) ─────
   It used to be a full-width band under EXPLORE ARCADE. Mr. Avery: "it doesn't
   need all that space at the bottom." Same pixel-frame chrome as the search box
   so the two read as one control cluster. The popover is an ABSOLUTE child of
   the bar (top:100% — it measures itself, no magic offset to keep in sync) and
   hangs over the scroll area below; it must never be a flow child, or it would
   stretch the bar's height. */
.wz-sugbtn {
  flex: none; display: flex; align-items: center; gap: 6px;
  margin-left: 8px; padding: 8px 11px;
  background: rgba(12,7,30,.85); background-clip: padding-box;
  border: 4px solid transparent;
  border-image: var(--px-cyan-dim) 4 stretch;
  color: var(--wz-gold); cursor: pointer;
  font: 700 11px 'JetBrains Mono', monospace; letter-spacing: .06em;
  transition: box-shadow .12s, color .12s;
}
.wz-sugbtn svg { width: 15px; height: 15px; flex: none; }
.wz-sugbtn:hover, .wz-sugbtn.wz-on {
  color: #fff; border-image-source: var(--px-cyan);
  box-shadow: 0 0 14px rgba(255,212,0,.35);
}
.wz-sugbtn:focus-visible { outline: 3px solid var(--wz-pink); outline-offset: 2px; }

.wz-sugpop {
  /* absolute insets resolve against the PADDING box, so `right: 0` would hang
     the bar's own side padding past the trigger — offset by it to line the
     popover's right edge up with the button's */
  position: absolute; z-index: 40; top: 100%; right: var(--wz-tb-pad, 14px);
  width: min(420px, calc(100% - var(--wz-tb-pad, 14px) * 2));
  margin-top: 6px;
  padding: 14px 16px 12px;
  background: rgba(8,4,22,.97); background-clip: padding-box;
  border: 4px solid transparent;
  border-image: var(--px-cyan) 4 stretch;
  box-shadow: 0 14px 40px rgba(0,0,0,.6), 0 0 22px rgba(22,224,224,.25);
}
.wz-sugpop-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 10px; }
.wz-sugpop-head .arc-suggest-head { flex: 1; margin: 0; font-size: 12px; }
.wz-sugpop-x {
  flex: none; background: none; border: none; cursor: pointer; padding: 0;
  color: rgba(233,240,255,.6); line-height: 0;
}
.wz-sugpop-x svg { width: 16px; height: 16px; }
.wz-sugpop-x:hover { color: var(--wz-pink); }
.wz-sugpop-x:focus-visible { outline: 2px solid var(--c); outline-offset: 2px; }

/* ── views + the one scroll container ────────────────────────────────────── */
.wz-scroll { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; }
.wz-scroll.wz-noscroll { overflow-y: hidden; }
.wz-scroll::-webkit-scrollbar { width: 10px; }
.wz-scroll::-webkit-scrollbar-track { background: rgba(0,0,0,.35); }
.wz-scroll::-webkit-scrollbar-thumb { background: var(--c); border: 2px solid #0a0618; }
/* author `display` would beat the UA [hidden] rule — see CLAUDE.md gotcha #4 */
.wz-view { display: block; }
.wz-view[hidden] { display: none !important; }

/* ── HOME: hero + IN THE ZONE ─────────────────────────────────────────────
   ⚠ align-items: STRETCH is load-bearing (2026-07-30). IN THE ZONE used to be
   an aspect-locked raster (981/1111) and the 2.56fr column ratio existed purely
   to make the two art panels' BOTTOMS coincide — their tops did too only by
   arithmetic. Mr. Avery asked for the two panels to be exactly the same height,
   tops AND bottoms. Now the hero's aspect-ratio sets the row height and the
   stretched zone matches it precisely, with its content flexing inside; the
   column ratio is free to be about how much room each panel WANTS. */
.wz-app .arc-top {
  grid-template-columns: minmax(0, 2.5fr) minmax(0, 1fr);
  gap: 14px;
  padding: 14px 14px 4px;
  border-bottom: none;
  min-height: 0;
  align-items: stretch;
}

/* ── the hero — CODE-DRAWN frame over a clip-pathed live well ─────────────
   z1 .wz-hero-well (live content, clipped to the ring's notched interior)
   z3 .wz-hero-frame (inline SVG, pointer-events:none — clicks fall through)

   ⚠ The frame is NOT an image any more. `_tools/warpzone/trace-hero.py` traces
   Mr. Avery's own sample (raw/hero-sample-run3.webp, 1029×456) into three
   rectilinear loops; the SVG paths live in js/arcade.js and the WELL loop is the
   clip-path below. The aspect-ratio is his sample's TRUE 1029/456 — if the
   polygons are ever re-traced, all four must be updated together. */
.wz-hero {
  position: relative;
  width: 100%;
  aspect-ratio: 1029 / 456;
  container-type: inline-size;
}
.wz-hero-frame {
  position: absolute; inset: 0; z-index: 3;
  width: 100%; height: 100%;
  pointer-events: none;
  user-select: none;
  /* CSS filters aren't clipped by the SVG viewport, so this is what gives the
     ring an outward neon halo; the in-SVG .wz-hf-glow stroke does the inward
     half. Cheaper than an feGaussianBlur filter primitive and static. */
  filter: drop-shadow(0 0 4px rgba(30,160,245,.42));
}
/* the dark band between the two lines (outer loop minus well loop, evenodd) */
.wz-hf-body { fill: #0a0618; fill-opacity: .94; }
.wz-hf-glow { fill: none; stroke: #1ea0f5; stroke-opacity: .15; stroke-width: 10; }
.wz-hf-line { fill: none; stroke-linejoin: miter; }
.wz-hf-out  { stroke: #1ea0f5; stroke-width: 4.5; }
.wz-hf-in   { stroke: #2f6fd8; stroke-width: 3; stroke-opacity: .55; }
.wz-hero-well {
  position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(140deg, #140b30, #0c0720);
  /* the WELL loop from trace-hero.py — the frame's exact notched interior */
  clip-path: polygon(1.65% 11.18%, 2.24% 11.18%, 2.24% 10.09%, 2.82% 10.09%, 2.82% 8.99%, 3.4% 8.99%, 3.4% 7.89%, 42.08% 7.89%, 42.08% 6.8%, 42.66% 6.8%, 42.66% 5.7%, 43.25% 5.7%, 43.25% 4.61%, 98.35% 4.61%, 98.35% 90.35%, 98.06% 90.35%, 98.06% 91.45%, 97.57% 91.45%, 97.57% 92.54%, 97.08% 92.54%, 97.08% 93.64%, 96.6% 93.64%, 96.6% 94.74%, 96.11% 94.74%, 96.11% 95.83%, 2.72% 95.83%, 2.72% 94.74%, 2.14% 94.74%, 2.14% 93.64%, 1.65% 93.64%);
}
.wz-hero-art {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
}
/* the mock's near-solid dark copy panel on the left ~45%, feathered into the art */
.wz-hero-art::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, #0a0618 0%, #0a0618 39%, rgba(10,6,24,.84) 46%, rgba(10,6,24,.3) 55%, transparent 65%);
}
/* ⚠ TOP-ANCHORED COPY, BOTTOM-ANCHORED BUTTONS (2026-07-30, Mr. Avery: "PLAY
   NOW + DETAILS in the exact same place on every slide"). The block used to be
   vertically CENTRED and the buttons flowed under variable-height copy, so the
   row jumped ~40px between RUN 3 and COOL MATH GAMES. Now the badge/title/tag/
   blurb stack from the top and .wz-hero-btns is absolutely positioned at the
   text column's bottom edge — identical y on every slide, by construction.
   `padding-bottom` on the column is what stops the copy running into it.
   ⚠ `top` is 9.4%, not the old 4.6%: since the riser moved to 27% the LOW tread
   of the frame's top edge now runs across the text column, and 4.6% would put
   the FEATURED badge under the frame band. */
.wz-hero-text {
  position: absolute; z-index: 2;
  left: 1.7%; top: 9.4%; bottom: 7.6%; width: 47%;
  display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start;
  gap: 0;
  padding: 0 1cqi 7.6cqi 3.4cqi;
  min-width: 0;
}
.wz-hero-badge {
  flex: none;
  border: 4px solid transparent;
  border-image: var(--px-cyan) 4 stretch;
  background: rgba(10,6,26,.78); background-clip: padding-box;
  color: var(--c); text-shadow: 0 0 8px rgba(22,224,224,.8);
  font: 700 max(8px, 1.42cqi) 'Pixeloid Sans', monospace; letter-spacing: .1em;
  padding: .55cqi 1.1cqi;
  margin-bottom: 1.5cqi;
}
/* font-size is set inline per slide (titleSize() in arcade.js) so long names
   wrap to two lines instead of three — see the note there. The 2-line clamp is
   the hard backstop: a title can crop, but it can never grow the column. */
.wz-hero-title {
  font-family: 'Minecraft', monospace; font-weight: 700;
  font-size: max(20px, 6.2cqi); line-height: 1.14;
  text-transform: uppercase;
  color: #fff; max-width: 100%;
  text-shadow: .09em .09em 0 #0a0618, 0 0 26px rgba(154,217,255,.55);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.wz-hero-tag {
  margin-top: 1.7cqi; max-width: 100%;
  font: 800 max(9px, 1.95cqi) 'JetBrains Mono', monospace;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--wz-pink); text-shadow: 0 0 10px rgba(255,46,126,.55);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.wz-hero-desc {
  margin: 1.4cqi 0 0; max-width: 36ch;
  font: 600 max(8px, 1.58cqi) 'JetBrains Mono', monospace;
  line-height: 1.5; color: rgba(233,240,255,.88);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.wz-hero-btns {
  position: absolute; left: 3.4cqi; bottom: 0; right: 1cqi;
  display: flex; align-items: center; gap: 1.6cqi; flex-wrap: nowrap;
}
.wz-hero-play, .wz-hero-details { white-space: nowrap; }
.wz-hero-play {
  background: var(--wz-gold); background-clip: padding-box;
  color: #1a1200; cursor: pointer;
  border: 4px solid transparent;
  border-image: var(--px-dark) 4 stretch;
  font: 700 max(10px, 1.95cqi) 'Pixeloid Sans', monospace;
  padding: 1.15cqi 1.9cqi;
  box-shadow: 0 0 18px rgba(255,212,0,.4);
  transition: box-shadow .12s, transform .08s;
}
.wz-hero-play:hover { background: #ffe14d; transform: translate(-2px,-2px); box-shadow: 0 0 26px rgba(255,212,0,.65); }
.wz-hero-details {
  background: rgba(10,6,26,.72); background-clip: padding-box;
  color: var(--c); cursor: pointer;
  border: 4px solid transparent;
  border-image: var(--px-cyan) 4 stretch;
  font: 700 max(9px, 1.52cqi) 'Pixeloid Sans', monospace;
  padding: 1.15cqi 1.7cqi;
  transition: color .1s, transform .08s, box-shadow .12s;
}
.wz-hero-details:hover { color: #fff; transform: translate(-2px,-2px); box-shadow: 0 0 16px rgba(22,224,224,.5); }
.wz-hero-details:focus-visible,
.wz-hero-play:focus-visible { outline: 3px solid var(--wz-pink); outline-offset: 3px; }

/* dots — square pixels near the well's bottom edge, gold active. x/y match the
   sample art (centre ≈54% of the panel, ≈93% down), not a guess. */
.wz-app .arc-hero-dots {
  position: absolute; bottom: 5.2%; left: 54%; transform: translateX(-50%);
  display: flex; gap: 1.3cqi; z-index: 3; margin: 0; padding: 0;
}
.wz-app .arc-dot {
  width: max(8px, 1.4cqi); height: max(8px, 1.4cqi);
  cursor: pointer; border-radius: 0;
  background: #4a63d8; box-shadow: 0 0 0 2px rgba(10,6,24,.85);
}
.wz-app .arc-dot:hover { background: #7fd9ff; }
.wz-app .arc-dot.active { background: var(--wz-gold); box-shadow: 0 0 0 2px rgba(10,6,24,.85), 0 0 9px rgba(255,212,0,.7); }
.wz-app .arc-dot:focus-visible { outline: 2px solid var(--c); outline-offset: 3px; }
/* ‹ › are NOT in his art — kept for keyboard/touch reach, tucked small into the
   well's bottom-right so they never compete with the dots or the art. */
.wz-hero-nav {
  position: absolute; z-index: 4; right: 4.5%; bottom: 9%;
  display: flex; gap: .6cqi;
}
.wz-hero-prev, .wz-hero-next {
  background: rgba(10,6,26,.7); background-clip: padding-box;
  border: 3px solid transparent; border-image: var(--px-cyan) 3 stretch;
  color: var(--c); cursor: pointer;
  padding: .2cqi .7cqi; font-size: max(10px, 1.5cqi); line-height: 1.1;
  opacity: .65; transition: opacity .12s, color .1s;
}
.wz-hero:hover .wz-hero-prev, .wz-hero:hover .wz-hero-next { opacity: 1; }
.wz-hero-prev:hover, .wz-hero-next:hover { color: #fff; box-shadow: 0 0 14px rgba(22,224,224,.55); }
.wz-hero-prev:focus-visible, .wz-hero-next:focus-visible { opacity: 1; outline: 3px solid var(--wz-pink); outline-offset: 2px; }

/* ── code-drawn panel frames (shared) ─────────────────────────────────────
   ONE generator in js/arcade.js (pixelFrameSVG / mountPixelFrame) draws the
   stepped-corner double line for IN THE ZONE and EXPLORE ARCADE, in the same
   language as the traced hero ring. Colours live HERE, per panel, as custom
   properties — the SVG itself is colourless so one helper serves every panel.
   The layer is z0 + pointer-events:none; panel content is z1 and must carry
   enough padding to clear the 20px corner staircase. */
.wz-pf { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.wz-pf-svg { display: block; width: 100%; height: 100%; overflow: visible; }
.wz-pf-body { fill: var(--pf-fill, rgba(10,6,26,.34)); }
.wz-pf-glow { fill: none; stroke: var(--pf-glow, transparent); stroke-width: 9; }
.wz-pf-line { fill: none; stroke-linejoin: miter; }
.wz-pf-out  { stroke: var(--pf-line, #8A6CFF); stroke-width: 4; }
.wz-pf-in   { stroke: var(--pf-line2, rgba(138,108,255,.5)); stroke-width: 2.5; }

/* ── IN THE ZONE — drawn frame + LIVE labels ──────────────────────────────
   Was Mr. Avery's opaque raster with baked-in labels and %-positioned wells;
   replaced 2026-07-30 (see the note in js/arcade.js). The panel STRETCHES to
   the hero's height — no aspect-ratio here, on purpose — and its two sections
   flex to fill whatever height that turns out to be.
   ⚠ Type is cqi-scaled off .wz-zone's own inline size, so it tracks the panel
   the way the baked labels used to track the art. */
/* ⚠ The zone is ABSOLUTE inside .wz-right so it contributes ZERO height to the
   grid row. The row height is then set purely by the hero (which has an
   aspect-ratio), the stretched .wz-right matches it, and the zone fills that —
   tops AND bottoms flush, which is what Mr. Avery asked for. If the zone were
   in normal flow its content's min-height would push the row taller than the
   hero and only the TOPS would line up (that was the first attempt's bug). */
.wz-right {
  position: relative;
  min-width: 0; min-height: 0;
}
.wz-zone {
  position: absolute; inset: 0; width: 100%;
  container-type: inline-size;
  --pf-fill: rgba(14,7,32,.72);
  --pf-line: #FF2E7E;
  --pf-line2: rgba(138,108,255,.62);
  --pf-glow: rgba(255,46,126,.16);
  filter: drop-shadow(0 0 4px rgba(255,46,126,.28));
}
.wz-zone-body {
  position: absolute; inset: 0; z-index: 1; min-height: 0;
  display: flex; flex-direction: column; gap: 2.2cqi;
  padding: 4.4cqi 4cqi 4.4cqi;
}
.wz-zone-head {
  flex: none; margin: 0;
  display: flex; align-items: center; gap: 1.6cqi;
  font: 700 max(11px, 4.1cqi) 'Pixeloid Sans', monospace;
  letter-spacing: .04em; text-transform: uppercase;
  color: #eafcff; text-shadow: 0 0 12px rgba(255,46,126,.55);
}
.wz-zone-ico { font-size: max(13px, 4.6cqi); line-height: 1; filter: drop-shadow(0 0 6px rgba(255,46,126,.7)); }

/* the two inner sections keep the v2 12×12 border-image pixel frames — the
   drawn frame is the PANEL language, these are sub-boxes (like cards/search) */
.wzz-box {
  min-height: 0; min-width: 0;
  display: flex; flex-direction: column;
  border: 4px solid transparent;
  border-image: var(--px-purple-dim) 4 stretch;
  background: rgba(8,4,22,.5); background-clip: padding-box;
  padding: 1.8cqi 2cqi 2cqi;
}
.wz-zone-recent-box { flex: 0 0 auto; }
.wz-zone-pop-box { flex: 1 1 auto; }
.wzz-label {
  flex: none;
  font: 700 max(10px, 2.9cqi) 'Pixeloid Sans', monospace;
  letter-spacing: .08em; color: var(--c);
  text-shadow: 0 0 8px rgba(22,224,224,.45);
}
.wzz-sechead {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 1cqi;
  margin-bottom: 1cqi;
}
.wz-zone-viewall {
  flex: none; cursor: pointer; padding: .6cqi 1.2cqi;
  background: rgba(255,46,126,.14); background-clip: padding-box;
  border: 3px solid transparent; border-image: var(--px-pink) 3 stretch;
  color: #ffa9cd; white-space: nowrap;
  font: 700 max(9px, 2.4cqi) 'Pixeloid Sans', monospace; letter-spacing: .06em;
  transition: color .12s, background .12s, box-shadow .12s;
}
.wz-zone-viewall:hover { color: #fff; background: rgba(255,46,126,.32); box-shadow: 0 0 12px rgba(255,46,126,.55); }
.wz-zone-viewall:focus-visible { outline: 2px solid var(--c); outline-offset: 2px; }

.wz-zone-recent { min-width: 0; margin-top: 1.2cqi; position: relative; }
.wzz-recent-row {
  display: flex; gap: 2cqi; align-items: flex-start;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none;
}
.wzz-recent-row::-webkit-scrollbar { height: 0; display: none; }
.wzz-rcard { flex: 0 0 16.5cqi; min-width: 0; text-align: center; cursor: pointer; }
.wzz-rimg {
  width: 100%; aspect-ratio: 1 / 1;
  background: #1a1a2e; background-size: cover; background-position: center;
  border: 2px solid rgba(138,108,255,.65);
}
.wzz-rcard:hover .wzz-rimg { border-color: var(--c); box-shadow: 0 0 10px rgba(22,224,224,.5); }
.wzz-rcard:focus-visible { outline: 2px solid var(--wz-pink); outline-offset: 2px; }
.wzz-rname {
  padding-top: .7cqi;
  font: 700 max(9px, 2.1cqi) 'JetBrains Mono', monospace;
  color: #eafcff; text-transform: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wzz-empty {
  display: flex; align-items: center; justify-content: center;
  padding: 3cqi 4cqi; text-align: center;
  font: 600 max(10px, 2.5cqi) 'JetBrains Mono', monospace;
  color: rgba(233,240,255,.55);
}

.wz-zone-pop {
  flex: 1; min-height: 0;
  display: flex; flex-direction: column; gap: .2cqi;
  overflow: hidden;
}
.wzz-poprow {
  flex: 1; min-height: 0;
  display: flex; align-items: center; gap: 1.6cqi;
  cursor: pointer;
}
.wzz-poprow:hover .wzz-pname { color: var(--c); }
.wzz-poprow:focus-visible { outline: 2px solid var(--wz-pink); outline-offset: 1px; }
.wzz-rank {
  flex: none;
  font: 800 max(10px, 2.9cqi) 'JetBrains Mono', monospace;
  color: var(--wz-pink);
}
.wzz-pname {
  flex: none; min-width: 0; max-width: 58%;
  font: 700 max(10px, 2.9cqi) 'JetBrains Mono', monospace;
  color: #eafcff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wzz-lead {
  flex: 1; align-self: flex-end;
  height: 1cqi; margin-bottom: 1.1cqi; min-width: 2cqi;
  background: radial-gradient(circle, rgba(233,240,255,.5) 26%, transparent 28%) 0 50% / 1cqi 1cqi repeat-x;
}
.wzz-count {
  flex: none;
  font: 800 max(10px, 2.7cqi) 'JetBrains Mono', monospace;
  color: var(--wz-pink); letter-spacing: .02em;
}

/* ── row scrollbars ───────────────────────────────────────────────────────
   Rows that have ‹ › arrows hide their scrollbar (the arrows are the
   affordance, and the mock shows none); rows WITHOUT arrows keep a slimmed
   one, so nothing loses its only scroll cue. */
.wz-app .wz-hasarrows .arc-row { scrollbar-width: none; }
.wz-app .wz-hasarrows .arc-row::-webkit-scrollbar { height: 0; display: none; }

.wz-app .arc-row::-webkit-scrollbar { height: 4px; }
.wz-app .arc-row::-webkit-scrollbar-track { background: rgba(255,255,255,.05); }
.wz-app .arc-row::-webkit-scrollbar-thumb { background: rgba(22,224,224,.35); }
.wz-app .arc-row:hover::-webkit-scrollbar-thumb { background: rgba(22,224,224,.7); }

/* ── row pagination — SIDE RAILS ──────────────────────────────────────────
   Rebuilt 2026-07-30. Each rail spans the row's FULL height (top/bottom 0 of
   .wz-rowwrap) instead of floating as a 42px box at a hand-tuned 54%, and it
   carries the scrim that fades the cards out underneath it — the fade is the
   "there's more this way" cue; the chip is the control. Both hide at the ends.
   ⚠ .wz-rowwrap is what makes `top: 0` mean THE ROW. Don't move the buttons
   back up to the section — see the note in js/arcade.js addRowArrows. */
.wz-hasarrows { position: relative; }
.wz-rowwrap { position: relative; }
.wz-rowarrow {
  position: absolute; top: 0; bottom: 0; z-index: 6;
  width: 52px; padding: 0; cursor: pointer;
  border: none; background: none;
  display: flex; align-items: center;
  color: var(--c);
}
.wz-rowarrow[hidden] { display: none; }
.wz-rowarrow-prev { left: 0; justify-content: flex-start; }
.wz-rowarrow-next { right: 0; justify-content: flex-end; }
/* the void, leaking in from the edge */
.wz-rowarrow-prev { background: linear-gradient(90deg, rgba(10,6,26,.96) 34%, rgba(10,6,26,0)); }
.wz-rowarrow-next { background: linear-gradient(270deg, rgba(10,6,26,.96) 34%, rgba(10,6,26,0)); }
.wz-rowarrow-chip {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 46px;
  background: rgba(10,6,26,.92); background-clip: padding-box;
  border: 4px solid transparent; border-image: var(--px-cyan-dim) 4 stretch;
  transition: transform .14s ease, box-shadow .14s ease;
}
.wz-rowarrow-chip svg { width: 15px; height: 15px; display: block; }
/* border-image-source can't animate (see the card note below) — swap it, and
   let the glow + nudge carry the motion */
.wz-rowarrow:hover .wz-rowarrow-chip {
  border-image-source: var(--px-cyan);
  box-shadow: 0 0 16px rgba(22,224,224,.55);
}
.wz-rowarrow:hover { color: var(--wz-gold); }
.wz-rowarrow-prev:hover .wz-rowarrow-chip { transform: translateX(-2px); }
.wz-rowarrow-next:hover .wz-rowarrow-chip { transform: translateX(2px); }
.wz-rowarrow:active .wz-rowarrow-chip { transform: none; }
.wz-rowarrow:focus-visible { outline: none; }
.wz-rowarrow:focus-visible .wz-rowarrow-chip { outline: 3px solid var(--wz-pink); outline-offset: 2px; }
/* inside the zone art everything scales with the panel */
.wz-zone-recent .wz-rowarrow { width: 9cqi; }
.wz-zone-recent .wz-rowarrow-chip { width: 5cqi; height: 9cqi; border-width: 3px; border-image-slice: 3; }
.wz-zone-recent .wz-rowarrow-chip svg { width: 3.4cqi; height: 3.4cqi; }

/* ── nav overflow arrows ───────────────────────────────────────────────────
   Same shape as .wz-rowarrow above, sized down for the tab strip (the nav row
   is ~35px tall vs a 46px card row). js/arcade.js's addNavArrows() wraps
   .wz-nav in .wz-navwrap (position:relative — see that class above) and
   appends these two buttons into it; both are position:absolute, so
   appearing/hiding them changes .wz-navwrap's box not at all, which is what
   keeps them from feeding back into the ResizeObserver that decides
   .wz-barwrap/.wz-narrow (js/arcade.js syncBar() — see the comment on that
   function for the trap this dodges). */
.wz-navarrow {
  position: absolute; top: 0; bottom: 0; z-index: 6;
  width: 30px; padding: 0; cursor: pointer;
  border: none; background: none;
  display: flex; align-items: center;
  color: var(--c);
}
.wz-navarrow[hidden] { display: none; }
.wz-navarrow-prev { left: 0; justify-content: flex-start; }
.wz-navarrow-next { right: 0; justify-content: flex-end; }
.wz-navarrow-prev { background: linear-gradient(90deg, rgba(10,6,26,.96) 40%, rgba(10,6,26,0)); }
.wz-navarrow-next { background: linear-gradient(270deg, rgba(10,6,26,.96) 40%, rgba(10,6,26,0)); }
.wz-navarrow-chip {
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 26px;
  background: rgba(10,6,26,.92); background-clip: padding-box;
  border: 3px solid transparent; border-image: var(--px-cyan-dim) 3 stretch;
  transition: transform .14s ease, box-shadow .14s ease;
}
.wz-navarrow-chip svg { width: 12px; height: 12px; display: block; }
.wz-navarrow:hover .wz-navarrow-chip {
  border-image-source: var(--px-cyan);
  box-shadow: 0 0 12px rgba(22,224,224,.5);
}
.wz-navarrow:hover { color: var(--wz-gold); }
.wz-navarrow-prev:hover .wz-navarrow-chip { transform: translateX(-2px); }
.wz-navarrow-next:hover .wz-navarrow-chip { transform: translateX(2px); }
.wz-navarrow:active .wz-navarrow-chip { transform: none; }
.wz-navarrow:focus-visible { outline: none; }
.wz-navarrow:focus-visible .wz-navarrow-chip { outline: 3px solid var(--wz-pink); outline-offset: 2px; }

/* ── cards — stepped pixel frames + ONE clean hover ───────────────────────
   ⚠ HOVER WAS BROKEN AND THIS IS THE FIX (2026-07-30). Three separate faults,
   all of them still easy to reintroduce:
   ① `.arc-row` is `overflow-x: auto`, which forces overflow-y to a NON-visible
      value — so the -3px lift was CLIPPED off the top of every horizontal row
      (EXPLORE, RECENTLY PLAYED) while working fine in the grids. The row now
      carries its own top/bottom padding so the lift has room INSIDE the
      scrollport. Never remove that padding.
   ② the hover swapped `border-image-source` dim→bright. border-image-source is
      not animatable, so it hard-cut on every enter/leave and flickered against
      the transform. The bright frame is now a cross-fading ::after overlay and
      the base border never changes.
   ③ windows.css's baseline `.arc-card:hover .arc-thumb { scale(1.04) }` still
      applies and is WANTED — it's the zoom half of the effect. It only looked
      like a fight because of ①. Lift + glow + thumb zoom + PLAY wash are one
      effect; keep them in sync.
   Cards no longer print a category line (`.wz-card-cat` deleted) — the tab
   above the grid already says it. */
.wz-app .arc-card {
  position: relative;
  background: rgba(12,7,30,.78); background-clip: padding-box;
  border: 4px solid transparent;
  border-image: var(--px-cyan-dim) 4 stretch;
  padding-bottom: 5px;
  transition: box-shadow .14s ease, transform .14s ease;
}
.wz-app .arc-card::after {
  /* inset = -(border width). Absolute insets resolve against the PADDING box,
     so -4px puts this element's border box exactly on the card's border box —
     the bright ring lands ON the dim one, not beside it. It's above the thumb
     (z2) so the 1.04 zoom can't spill over the frame. */
  content: ''; position: absolute; inset: -4px; z-index: 2; pointer-events: none;
  border: 4px solid transparent;
  border-image: var(--px-cyan) 4 stretch;
  opacity: 0; transition: opacity .14s ease;
}
.wz-app .arc-card:hover,
.wz-app .arc-card:focus-visible {
  box-shadow: 0 0 22px rgba(22,224,224,.55);
  transform: translateY(-5px);
}
.wz-app .arc-card:hover::after,
.wz-app .arc-card:focus-visible::after { opacity: 1; }

/* ── HOVER, part two: THE CABINET LIGHTS UP (2026-07-30) ──────────────────
   The old treatment dropped a 60%-black wash over the whole thumb to print
   PLAY on it — hovering a game HID the game, which is backwards for a shelf a
   kid is browsing by its art. Now the art gets BRIGHTER and a gold ▶ PLAY tab
   rises from the bottom edge of the screen, borrowing the hero's ▶ PLAY NOW
   grammar one size down. Scrim only behind the tab, never over the picture.
   One effect, four parts: lift + ignite + brighten + tab. Keep them in sync. */
.wz-app .arc-play {
  inset: auto 0 0 0;
  padding: 20px 0 6px;
  align-items: flex-end;
  background: linear-gradient(0deg, rgba(6,3,16,.94) 22%, rgba(6,3,16,0));
  font: 700 10px 'Pixeloid Sans', monospace; letter-spacing: .1em;
  color: var(--wz-gold); text-shadow: 0 0 9px rgba(255,212,0,.55);
  transform: translateY(100%);
  transition: opacity .16s ease, transform .18s cubic-bezier(.2,.8,.3,1);
}
.wz-app .arc-card:hover .arc-play,
.wz-app .arc-card:focus-visible .arc-play { opacity: 1; transform: none; }
.wz-app .arc-thumb { transition: transform .18s ease, filter .18s ease; }
.wz-app .arc-card:hover .arc-thumb,
.wz-app .arc-card:focus-visible .arc-thumb {
  transform: scale(1.06);
  filter: saturate(1.18) brightness(1.1);
}
.wz-app .arc-card:hover .arc-name,
.wz-app .arc-card:focus-visible .arc-name { color: #fff; }
.wz-app .arc-card:focus-visible { outline: 3px solid var(--wz-pink); outline-offset: 2px; }
/* room for the lift + focus rings inside the clipping scrollport (①) */
.wz-app .arc-row { padding: 8px 5px 12px; }
.wz-app .wz-grid { padding-top: 14px; }
.wz-app .arc-name {
  font: 800 10px 'JetBrains Mono', monospace; text-transform: uppercase;
  letter-spacing: .04em;
}
/* DETAILS jump target */
.wz-flash { animation: wz-flash 1.6s ease-out 1; }
@keyframes wz-flash {
  0%, 100% { box-shadow: 0 0 16px rgba(22,224,224,.45); }
  20%, 60% { box-shadow: 0 0 0 4px var(--wz-gold), 0 0 26px rgba(255,212,0,.7); }
}

/* EXPLORE ARCADE — the SAME code-drawn frame as the hero + IN THE ZONE.
   (Was a 12×12 border-image pixel frame; swapped 2026-07-30 so all three HOME
   panels speak one framing language. Padding must clear the 20px corner
   staircase, hence 20px sides.) */
.wz-explore {
  position: relative;
  margin: 12px 14px 10px; padding: 16px 20px 14px;
  --pf-fill: rgba(10,6,26,.34);
  --pf-line: #8A6CFF;
  --pf-line2: rgba(138,108,255,.42);
  --pf-glow: rgba(138,108,255,.12);
}
.wz-explore > *:not(.wz-pf) { position: relative; z-index: 1; }
.wz-app .arc-section-head {
  font: 700 13px 'Pixeloid Sans', monospace;   /* was 10px PS2P */
  color: var(--c); text-shadow: 0 0 10px rgba(22,224,224,.45);
  text-transform: uppercase;
  margin-bottom: 6px;
}

/* ── category tabs + ALL GAMES: card grid views ──────────────────────────── */
.wz-view[data-view^="cat:"], .wz-view[data-view="all"] { padding: 12px 16px 20px; }
.wz-cat-head {
  display: flex; align-items: baseline; gap: 10px;
  font: 700 15px 'Pixeloid Sans', monospace;   /* was 12px PS2P */
  color: var(--c); text-shadow: 0 0 10px rgba(22,224,224,.45);
  padding: 6px 2px 2px;
}
.wz-cat-ico { font-size: 18px; line-height: 1; filter: drop-shadow(0 0 8px rgba(22,224,224,.55)); }
.wz-cat-n { font: 800 10px 'JetBrains Mono', monospace; color: var(--wz-gold); letter-spacing: .08em; }
.wz-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px;
  padding-top: 10px;
}
.wz-search-empty {
  padding: 34px 12px; text-align: center;
  font: 700 13px 'JetBrains Mono', monospace; color: rgba(233,240,255,.7);
}

/* ── AVERY ARCADE tab (the adopted cabinet) ──────────────────────────────
   The view is a fixed viewport: .wz-noscroll on .wz-scroll freezes the page
   scroll while it's up. The cabinet's own CSS is re-scoped from the retired
   .w-gamebox window to .wz-gbview (see css/windows.css). */
.wz-scroll { display: flex; flex-direction: column; }
.wz-view { flex: none; }
.wz-view.wz-gbview { flex: 1; min-height: 0; height: auto; padding: 0; }
.wz-view.wz-gbview[hidden] { flex: none; }
.wz-gb-missing {
  padding: 46px 20px; text-align: center;
  font: 700 13px 'Pixeloid Sans', monospace; line-height: 1.9;
  color: var(--wz-pink);
}

/* ── narrow app window ─────────────────────────────────────────────────────
   ⚠ CLASSES from a ResizeObserver, NOT @media — media queries measure the
   VIEWPORT, but this is a draggable OS window (see header note). */
.wz-app.wz-narrow .arc-top { grid-template-columns: 1fr; }
/* ⚠ Un-do the absolute zone when the columns STACK. Taking the zone out of flow
   is what makes it match the hero's height side-by-side (see .wz-right above),
   but in a single column that leaves a zero-height grid row and the panel draws
   straight over EXPLORE ARCADE. Stacked, it goes back into normal flow and
   sizes to its own content — the ResizeObserver redraws the frame either way. */
/* The hero's aspect is FIXED (it's a trace of his frame), so full-width in one
   column means a 940×416 hero that fills a 700px-tall window on its own and
   pushes IN THE ZONE and EXPLORE ARCADE entirely below the fold. Capping the
   width caps the height with it, and keeps the hero at roughly the same size it
   has in the two-column layout instead of ballooning as the window shrinks. */
.wz-app.wz-narrow .wz-hero { max-width: 760px; margin: 0 auto; }
.wz-app.wz-narrow .wz-right { position: static; width: 100%; max-width: 440px; margin: 0 auto; }
.wz-app.wz-narrow .wz-zone { position: relative; inset: auto; }
.wz-app.wz-narrow .wz-zone-body { position: relative; inset: auto; gap: 10px; }
.wz-app.wz-narrow .wz-zone-pop { min-height: 150px; }
/* ── the top bar's own breakpoint ─────────────────────────────────────────
   .wz-barwrap is MEASURED, not a width guess: js/arcade.js compares the tabs'
   real total against the room the brand/search/suggest leave, and drops the nav
   to its own full-width line one step before it would clip. It fires
   independently of .wz-narrow because the bar runs out of room at a different
   size than the HOME columns do (long category sets, a wide logo, a zoomed-in
   Chromebook). .wz-navscroll then adds the fade for the case where even a
   full-width nav can't hold every tab — a silently-scrolling tab strip is how
   CREATIVE / STRATEGY / MORE used to vanish with no cue at all. */
.wz-app:is(.wz-narrow, .wz-barwrap) .wz-topbar { flex-wrap: wrap; gap: 8px 14px; }
/* targets .wz-navwrap, not .wz-nav: the wrapper is the actual flex CHILD of
   .wz-topbar now (see the .wz-navwrap comment above .wz-nav) — .wz-nav is a
   nested block and order/flex-basis on it would do nothing here. */
.wz-app:is(.wz-narrow, .wz-barwrap) .wz-navwrap { order: 3; flex-basis: 100%; }
.wz-app:is(.wz-narrow, .wz-barwrap) .wz-search { margin-left: auto; }
/* The fade is the ambient "there's more" cue — it stays even now that the ‹›
   rail below gives kids an actual control, same reasoning as the content-row
   fade+chip pairing further down. Two cues, because a gradient alone doesn't
   read as "scrollable" to every 8-year-old, and an arrow alone is easy to
   miss if you're not looking at the edge of the bar. Masking changes no
   layout, so it still can't feed back into the ResizeObserver in
   js/arcade.js's syncBar() the way the comment above that function warns. */
.wz-app.wz-navscroll .wz-nav {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
}

.wz-app.wz-tiny .wz-logo { height: 34px; }
.wz-app.wz-tiny .wz-search input { width: 90px; font-size: 10px; }
/* the bulb alone still reads as "suggest" (it keeps its title + the popover
   header says it in full); the word is what stops fitting first */
.wz-app.wz-tiny .wz-sugbtn-txt { display: none; }
.wz-app.wz-tiny .wz-sugbtn { padding: 8px; margin-left: 6px; }
.wz-app.wz-tiny .wz-tab { font-size: 10px; padding: 8px 6px 6px; }
.wz-app.wz-tiny .wz-topbar { padding: 8px 10px 6px; gap: 8px; --wz-tb-pad: 10px; }
.wz-app.wz-tiny .wz-hero-desc, .wz-app.wz-tiny .wz-hero-badge { display: none; }
/* the two hero buttons hit their px floors long before the panel does, so
   without this they wrap onto two rows and crowd the frame's bottom edge */
.wz-app.wz-tiny .wz-hero-btns { gap: 6px; margin-top: 8px; flex-wrap: nowrap; }
.wz-app.wz-tiny .wz-hero-play { font-size: 9px; padding: 5px 8px; }
.wz-app.wz-tiny .wz-hero-details { font-size: 8px; padding: 5px 7px; }
.wz-app.wz-tiny .wz-hero-tag { font-size: 8px; margin-top: 6px; }
/* a 52px rail is a quarter of a 400px row — shrink the scrim, keep the chip
   at a real 30px tap target */
.wz-app.wz-tiny .wz-rowarrow { width: 38px; }
/* the tab row itself shrinks at .wz-tiny (.wz-tab font-size 10px above) */
.wz-app.wz-tiny .wz-navarrow { width: 22px; }
.wz-app.wz-tiny .wz-navarrow-chip { width: 15px; height: 20px; }
.wz-app.wz-tiny .wz-explore { margin: 10px 8px; padding: 14px 14px 12px; }

@media (prefers-reduced-motion: reduce) {
  .wz-stars { animation: none; }
  .wz-app.wz-in { animation: none; }
  .wz-flash { animation: none; box-shadow: 0 0 0 4px var(--wz-gold) !important; }
  /* the frame, the ▶ PLAY tab and the brightening still happen — only the
     movement stops, so the hover keeps saying the same thing */
  .wz-app .arc-card:hover, .wz-app .arc-card:focus-visible { transform: none; }
  .wz-app .arc-card:hover .arc-thumb,
  .wz-app .arc-card:focus-visible .arc-thumb { transform: none; }
  .wz-app .arc-play { transform: none; }
  .wz-rowarrow:hover .wz-rowarrow-chip { transform: none; }
  .wz-navarrow:hover .wz-navarrow-chip { transform: none; }
}
