/*
 * Pixel Kit: the Pixel Frame COMPONENTS (tokens, frame, buttons, avatar, editor card, tabs,
 * option grid, swatches). Page-agnostic: used by the signup stage (/auth/join, with
 * pixel-frame.css for the full-screen stage layout on top) and embedded on dashboard pages
 * (the pixel person editor in Settings > Profile). A page sets --pp-brand; the rest derives.
 * Design: canvas "PixelPerfect Signup — Pixel UI Kit & Flow".
 */
/* Derived from --pp-brand WHERE it is set: on :root for full pages (/auth/join), on a .pp-scope
   element when the kit is embedded in a page with its own theme (Settings > Profile). A custom
   property derived on :root does not recompute for a descendant that sets --pp-brand. */
:root, .pp-scope {
    --pp-panel: color-mix(in srgb, var(--pp-brand) 70%, #000);
    --pp-tile: color-mix(in srgb, var(--pp-brand) 82%, #000);
    --pp-drop: color-mix(in srgb, var(--pp-brand) 52%, #000);
    --pp-ink: #1d1d1f; --pp-paper: #fff; --pp-gold: #ffc83d; --pp-soft: #ffd9df;
    --pp-display: 'Press Start 2P', ui-monospace, monospace;
    --pp-text: 'Pixelify Sans', ui-monospace, monospace;
}
/* The frame. --line is the border colour, --rise the shadow depth (4px = hidden under the border). */
.pf { --line: var(--pp-ink); --rise: 8px; margin: 4px; border: 0; border-radius: 0;
      box-shadow: 0 -4px 0 0 var(--line), 0 4px 0 0 var(--line), -4px 0 0 0 var(--line), 4px 0 0 0 var(--line), 0 var(--rise) 0 0 var(--pp-drop); }
.pf-hero { --rise: 12px; }
.pf:focus-visible { --line: var(--pp-gold); outline: none; }
.pf[aria-checked="true"], .pf[aria-selected="true"], .pf.is-pressed { --line: #fff; --rise: 4px; transform: translateY(4px); }
button.pf { cursor: pointer; }
button.pf:active { --rise: 4px; transform: translateY(4px); }
.pp-avatar { --av: min(240px, 34dvh, 64vw); width: var(--av); height: var(--av); background: var(--pp-tile); display: grid; place-items: center; }
.pp-avatar svg { width: 92%; height: 92%; shape-rendering: crispEdges; display: block; }
.pp-avatar.pp-squash { transform: scale(.94); transition: transform 30ms ease-in; }
.pp-avatar.pp-rebound { transition: transform 150ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.pp-avatar svg.pp-fade { animation: pp-fade 90ms ease; }
@keyframes pp-fade { from { opacity: .35; } to { opacity: 1; } }
.pp-hint { margin: 0; font-size: 16px; }
.pp-row { display: flex; gap: 16px; align-items: center; justify-content: center; flex-wrap: wrap; }

/* Buttons: primary = Paper (the one move-on action), secondary = Tile. */
.pp-btn { appearance: none; padding: 16px 20px; min-height: 52px; background: var(--pp-paper); color: var(--pp-brand);
          font-family: var(--pp-display); font-size: 13px; line-height: 1.4; white-space: nowrap; }
.pp-btn-2 { background: var(--pp-tile); color: #fff; }
.pp-btn[hidden] { display: none; }
.pp-btn .pp-play { display: inline-block; margin-right: 10px; }
.pp-btn:hover .pp-play, .pp-btn:focus-visible .pp-play { animation: pp-nudge .6s steps(2) infinite; }
@keyframes pp-nudge { 50% { transform: translateX(4px); } }
.pp-reveal { animation: pp-in .3s steps(3, end) both; }
@keyframes pp-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
/* Beat 2: the card. Tabs, a grid of the player's OWN face with one thing swapped, the
   group's colour, then the actions; only the grid gives way (it scrolls DOWN, never sideways). */
.pp-beat-2 { flex: 1 1 auto; }
.pp-panel { width: min(358px, calc(100vw - 40px)); box-sizing: border-box; min-height: 0; flex: 0 1 auto; padding: 12px;
            background: var(--pp-panel); display: flex; flex-direction: column; align-items: stretch; gap: 12px; }
/* Everything in the panel keeps its size except the one thing that scrolls: the pixel
   side's .pp-strip, and .pp-parts, which is the cartoon side's equivalent. Leaving
   .pp-parts off this list outspecified its own `flex: 0 1 auto` below (0,4,0 beats
   0,1,0), so the mixer grew past the panel's border instead of scrolling inside it. */
.pp-panel > :not(.pp-strip):not(.pp-parts):not(.pp-board):not(.pp-fields) { flex-shrink: 0; }
.pp-tabs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px 10px; padding: 4px 4px 8px; }
.pp-tab { appearance: none; margin: 0; padding: 7px 4px; min-height: 36px; background: var(--pp-tile); color: #fff;
          font-family: var(--pp-text); font-size: 15px; font-weight: 600; white-space: nowrap; }
.pp-tab[aria-selected="true"] { background: var(--pp-paper); color: var(--pp-brand); }
.pp-strip { flex: 0 1 auto; min-height: 0; display: grid; grid-template-columns: repeat(auto-fill, 48px); grid-auto-rows: 48px; gap: 16px;
            justify-content: center; padding: 8px 8px 12px; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; }
.pp-opt { appearance: none; margin: 0; padding: 0; width: 48px; height: 48px; min-height: 0; background: var(--pp-tile); display: grid; place-items: center; }
.pp-opt svg { width: 88%; height: 88%; shape-rendering: crispEdges; display: block; }
.pp-opt .pp-none { font-family: var(--pp-display); font-size: 8px; color: #fff; line-height: 1.4; }
.pp-swatches { display: flex; flex-wrap: wrap; justify-content: center; gap: 0; }
.pp-swatches:empty { display: none; }
/* 44px tall hit area; the swatch itself is a framed 14px square inside it. */
.pp-swatch { appearance: none; position: relative; width: 26px; height: 44px; min-height: 44px; margin: 0; padding: 0; border: 0; background: transparent; cursor: pointer; }
.pp-swatch::before { content: ''; position: absolute; left: 6px; top: 13px; width: 14px; height: 14px; background: var(--c);
                     box-shadow: 0 -4px 0 0 var(--pp-ink), 0 4px 0 0 var(--pp-ink), -4px 0 0 0 var(--pp-ink), 4px 0 0 0 var(--pp-ink), 0 8px 0 0 var(--pp-drop); }
.pp-swatch[aria-checked="true"]::before { top: 17px; box-shadow: 0 -4px 0 0 #fff, 0 4px 0 0 #fff, -4px 0 0 0 #fff, 4px 0 0 0 #fff; }
.pp-swatch:focus-visible { outline: none; }
.pp-swatch:focus-visible::before { box-shadow: 0 -4px 0 0 var(--pp-gold), 0 4px 0 0 var(--pp-gold), -4px 0 0 0 var(--pp-gold), 4px 0 0 0 var(--pp-gold); }
.pp-btn:disabled { opacity: .5; cursor: not-allowed; --rise: 4px; }
.pp-panel .pp-row { flex-wrap: nowrap; }
.pp-btn.pp-icon { padding-left: 16px; padding-right: 16px; }

/* ── Cartoon style (hubs that offer the fighters) ─────────────────────── */
/* Style switch: two Pixel Frame buttons; the pressed one is the style in use. */
.pp-style { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 8px; }
.pp-style .pp-btn.is-on { background: var(--pp-paper); color: var(--pp-brand); }
/* A fighter is smooth line art and a full body: contain, never crisp-edged. The
   live one is a canvas the mixer repaints; the snapshot on the board is an img. */
.pp-avatar canvas { width: 92%; height: 92%; display: block; image-rendering: auto; }
.pp-board-row .pp-fighter-img { width: 36px; height: 36px; object-fit: contain; image-rendering: auto; }

/* The part mixer (public/js/avatar-parts.js). Its .ap-* markup is shared with
   /my/avatar and /embed/avatar, so it is skinned here rather than forked — the
   Pixel Frame look is this page's, not the picker's. */
.pp-parts { flex: 0 1 auto; min-height: 0; padding: 8px 8px 12px; overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; }
.pp-parts[hidden] { display: none; }
/* The mixer's own wrappers (.ap > .ap-controls) are unstyled block divs, so when the
   panel is taller than the mixer's content the slack lands INSIDE the scroller and
   reads as a hole under the tabs. Carrying the column down through both wrappers to
   the option grid lets the grid take that slack and centre its rows in it instead.
   `flex: 1 0 auto` and not `1 1 auto`: the 180-tile Body grid is far taller than the
   panel and must be allowed to overflow into the scroll, never squashed into it. */
.pp-parts { display: flex; flex-direction: column; }
.pp-parts > .ap,
.pp-parts .ap-controls { flex: 1 0 auto; display: flex; flex-direction: column; min-height: 0; }
.pp-parts .ap-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px; }
.pp-parts .ap-label { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; opacity: .7; }
.pp-parts .ap-group,
.pp-parts .ap-tint-reset { appearance: none; margin: 0; padding: 6px 10px; border: 0; background: var(--pp-tile); color: inherit; font: inherit; font-size: 11px; cursor: pointer; }
.pp-parts .ap-group.is-selected { background: var(--pp-paper); color: var(--pp-brand); }
.pp-parts .ap-options { flex: 1 0 auto; display: grid; grid-template-columns: repeat(auto-fill, 64px); grid-auto-rows: 64px; gap: 14px; justify-content: center; align-content: safe center; margin-bottom: 8px; }
.pp-parts .ap-option { appearance: none; margin: 0; padding: 4px; border: 0; width: 64px; height: 64px; background: var(--pp-tile); display: grid; place-items: center; cursor: pointer; }
.pp-parts .ap-option.is-selected { background: var(--pp-paper); }
/* One rule for both kinds of option art: a live-painted canvas for the part groups,
   a published thumbnail img for the Body grid (which spans every skeleton and so
   cannot be painted from the one loaded atlas). object-fit matters only to the img. */
.pp-parts .ap-option-art { width: 100%; height: 100%; display: block; object-fit: contain; }
.pp-parts .ap-tint-input { width: 34px; height: 26px; padding: 0; border: 0; background: none; cursor: pointer; }
.pp-parts .ap-status { margin: 0; font-size: 11px; opacity: .75; }

/* One column (below the pp-pair breakpoint): the group chips become a single-line strip
   that scrolls sideways. Wrapped, up to ten chips are several rows and push the part
   thumbnails — the thing the mixer is FOR — out of the panel's scroll view, so a phone
   opened onto the mixer and showed no parts at all. The tint row is left wrapping: it
   is three items. */
@media (max-width: 899px) {
    .pp-parts .ap-groups {
        flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
    }
    .pp-parts .ap-groups::-webkit-scrollbar { display: none; }
    .pp-parts .ap-groups > * { flex: 0 0 auto; }
}
