/*
 * PhoneRemo components (spec §9.5). Class names are BEM-ish `pr-*`:
 * block `pr-btn`, modifier `pr-btn--primary`, element `pr-btn__icon`, state
 * `is-*` or ARIA attributes. Load after tokens.css and base.css; tv.css and
 * phone.css then adjust the scale per device.
 *
 * Contents: button, icon button, card, badge, player chip, dot, QR panel,
 * connection pill, toast, banner, spinner, pulse, kbd, logo, blocker, game
 * card, dialog and sheet, segmented control, toggle, slider, language
 * switch, code block, header and footer, phone controls.
 *
 * Design notes ("living-room premium", §9.1): dark layered surfaces with a
 * hairline border and a 1 px top highlight; one violet accent; mint means
 * connected/live; depth from soft glows rather than hard shadows; springs on
 * focus. Logical properties everywhere, so RTL needs no overrides.
 */

/* ===========================================================================
 * Button  .pr-btn  (--primary | --secondary | --ghost | --danger) (--sm | --lg | --tv)
 * Icon slot: <svg class="pr-icon pr-btn__icon">. Loading: .is-loading or aria-busy="true".
 * ======================================================================== */
.pr-btn {
  --btn-h: 48px;
  --btn-px: var(--s-5);
  --btn-bg: var(--surface-3);
  --btn-bg-hover: color-mix(in oklab, var(--surface-3) 80%, var(--ink-500));
  --btn-fg: var(--text);
  --btn-border: var(--border);
  --btn-glow: none;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-block-size: max(var(--btn-h), var(--target-min));
  padding-inline: var(--btn-px);
  padding-block: 0;
  border: 1px solid var(--btn-border);
  border-radius: var(--r-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  line-height: 1;
  letter-spacing: .005em;
  text-decoration: none;
  white-space: nowrap;
  user-select: none;
  box-shadow: var(--highlight-inset);
  transition:
    background-color var(--dur-1) var(--ease-out),
    border-color var(--dur-1) var(--ease-out),
    box-shadow var(--dur-2) var(--ease-out),
    transform var(--dur-2) var(--ease-spring),
    color var(--dur-1) var(--ease-out);
}
.pr-btn:hover { background: var(--btn-bg-hover); color: var(--btn-fg); }
.pr-btn:active { transform: scale(.97); transition-duration: var(--dur-1); }
.pr-btn:focus-visible { box-shadow: var(--highlight-inset), var(--btn-glow, none); }
/* Remote / phone-pointer focus (kit menus set .is-focused whatever the input). */
.pr-btn.is-focused {
  outline: var(--ring-width) solid var(--focus);
  outline-offset: var(--ring-offset);
  box-shadow: var(--highlight-inset), var(--glow-accent);
  transform: scale(var(--focus-scale));
}
.pr-btn:disabled, .pr-btn[aria-disabled="true"] { opacity: .45; pointer-events: none; box-shadow: none; }

.pr-btn--primary {
  --btn-bg: var(--gradient-accent);
  --btn-bg-hover: linear-gradient(180deg, var(--violet-300) -40%, var(--violet-400) 45%, var(--violet-500) 120%);
  --btn-fg: var(--on-accent);
  --btn-border: color-mix(in oklab, var(--violet-300) 35%, transparent);
  --btn-glow: var(--glow-accent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .22), 0 6px 24px -10px color-mix(in oklab, var(--violet-500) 80%, transparent);
  text-shadow: 0 1px 1px rgb(0 0 0 / .2);
}
.pr-btn--primary:hover { box-shadow: inset 0 1px 0 rgb(255 255 255 / .26), var(--glow-accent); }
.pr-btn--primary:active { background: var(--accent-press); }
.pr-btn--secondary {
  --btn-bg: var(--surface-2);
  --btn-bg-hover: var(--surface-3);
  --btn-border: var(--border-strong);
}
.pr-btn--ghost {
  --btn-bg: transparent;
  --btn-bg-hover: color-mix(in oklab, var(--ink-300) 10%, transparent);
  --btn-border: transparent;
  --btn-fg: var(--text-muted);
  box-shadow: none;
}
.pr-btn--ghost:hover { --btn-fg: var(--text); }
.pr-btn--danger {
  --btn-bg: color-mix(in oklab, var(--red-500) 16%, var(--surface-2));
  --btn-bg-hover: color-mix(in oklab, var(--red-500) 26%, var(--surface-2));
  --btn-border: color-mix(in oklab, var(--red-500) 45%, transparent);
  --btn-fg: color-mix(in oklab, var(--red-500) 45%, var(--ink-50));
  --btn-glow: var(--glow-danger);
}
.pr-btn--sm { --btn-h: 36px; --btn-px: var(--s-4); font-size: var(--fs-sm); gap: var(--s-1); }
.pr-btn--lg { --btn-h: 60px; --btn-px: var(--s-6); font-size: var(--fs-lg); }
.pr-btn--tv { --btn-h: 72px; --btn-px: var(--s-7); font-size: var(--fs-xl); gap: var(--s-3); }
.pr-btn--block { display: flex; inline-size: 100%; }
.pr-btn__icon { --icon-size: 1.2em; margin-inline-start: -.15em; }
.pr-btn__icon--end { margin-inline: 0 -.15em; }

/* Loading: the label keeps its width, a spinner sits on top. */
.pr-btn.is-loading, .pr-btn[aria-busy="true"] { color: transparent; pointer-events: none; text-shadow: none; }
.pr-btn.is-loading > *, .pr-btn[aria-busy="true"] > * { opacity: 0; } /* keeps the accessible name */
.pr-btn.is-loading::after, .pr-btn[aria-busy="true"]::after {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  inline-size: 1.15em;
  block-size: 1.15em;
  border-radius: 50%;
  border: 2px solid color-mix(in oklab, var(--btn-fg) 30%, transparent);
  border-block-start-color: var(--btn-fg);
  animation: pr-spin .8s linear infinite;
}

/* ===========================================================================
 * Icon button  .pr-iconbtn  (always needs aria-label)
 * ======================================================================== */
.pr-iconbtn {
  --ib-size: 44px;
  display: inline-grid;
  place-items: center;
  inline-size: max(var(--ib-size), var(--target-min));
  block-size: max(var(--ib-size), var(--target-min));
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text-muted);
  box-shadow: var(--highlight-inset);
  transition: background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out),
    transform var(--dur-2) var(--ease-spring), box-shadow var(--dur-2) var(--ease-out);
}
.pr-iconbtn:hover { background: var(--surface-3); color: var(--text); }
.pr-iconbtn:active { transform: scale(.94); }
.pr-iconbtn:focus-visible { color: var(--text); box-shadow: var(--glow-accent); }
.pr-iconbtn[aria-pressed="true"] { color: var(--on-accent); background: var(--accent); border-color: transparent; }
.pr-iconbtn--ghost { background: transparent; border-color: transparent; box-shadow: none; }
.pr-iconbtn--sm { --ib-size: 36px; --icon-size: 18px; }
.pr-iconbtn--lg { --ib-size: 56px; --icon-size: 26px; }

/* ===========================================================================
 * Card  .pr-card, .pr-card--interactive
 * ======================================================================== */
.pr-card {
  position: relative;
  padding: var(--card-pad, var(--s-6));
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--gradient-surface);
  color: var(--text);
  box-shadow: var(--highlight-inset), var(--shadow-1);
}
.pr-card--flat { background: var(--surface); box-shadow: var(--highlight-inset); }
.pr-card--glass { background: var(--surface-overlay); box-shadow: var(--highlight-inset), var(--shadow-2); }
.pr-card--interactive {
  cursor: pointer;
  transition: transform var(--dur-3) var(--ease-spring), box-shadow var(--dur-2) var(--ease-out),
    border-color var(--dur-2) var(--ease-out);
}
.pr-card--interactive:hover,
.pr-card--interactive:focus-visible,
.pr-card--interactive.is-focused {
  transform: translateY(-4px) scale(var(--focus-scale));
  border-color: color-mix(in oklab, var(--violet-400) 55%, transparent);
  box-shadow: var(--highlight-inset), var(--glow-accent);
  outline: none;
}
.pr-card--interactive:focus-visible { outline: var(--ring-width) solid var(--focus); outline-offset: var(--ring-offset); }
.pr-card--interactive:active { transform: translateY(-1px) scale(.99); }
.pr-card__title { font-size: var(--fs-xl); font-weight: var(--fw-bold); line-height: var(--lh-snug); }
.pr-card__body { color: var(--text-muted); }

/* ===========================================================================
 * Badge  .pr-badge (--accent | --signal | --warning | --danger | --muted)
 * ======================================================================== */
.pr-badge {
  --badge-c: var(--ink-300);
  display: inline-flex;
  align-items: center;
  gap: .35em;
  min-block-size: 1.6em;
  padding: .1em .6em;
  border-radius: var(--r-pill);
  border: 1px solid color-mix(in oklab, var(--badge-c) 34%, transparent);
  background: color-mix(in oklab, var(--badge-c) 14%, transparent);
  color: color-mix(in oklab, var(--badge-c) 70%, var(--ink-50));
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  white-space: nowrap;
}
[data-theme="light"] .pr-badge { color: color-mix(in oklab, var(--badge-c) 60%, var(--ink-950)); }
.pr-badge--accent { --badge-c: var(--violet-400); }
.pr-badge--signal { --badge-c: var(--mint-500); }
.pr-badge--warning { --badge-c: var(--amber-500); }
.pr-badge--danger { --badge-c: var(--red-500); }
.pr-badge--mono { font-family: var(--font-mono); letter-spacing: .04em; }

/* ===========================================================================
 * Player chip  .pr-chip  (color dot + glyph, label, link badge, away, seat)
 *   <div class="pr-chip pr-p2">
 *     <span class="pr-chip__dot"><svg class="pr-icon"><use href="/ds/icons.svg#g-square"/></svg></span>
 *     <span class="pr-chip__label">Noa</span>
 *     <span class="pr-chip__score">120</span>
 *     <span class="pr-chip__link pr-chip__link--p2p">P2P</span>
 *   </div>
 * States: .pr-chip--away (disconnected, grace), .pr-chip--waiting (not seated), .pr-chip--compact.
 * ======================================================================== */
.pr-chip {
  --chip-h: 44px;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-block-size: var(--chip-h);
  max-inline-size: 100%;
  padding-block: 4px;
  padding-inline: 4px var(--s-4);
  border-radius: var(--r-pill);
  border: 1px solid color-mix(in oklab, var(--pc, var(--p0)) 30%, var(--border));
  background:
    linear-gradient(90deg, color-mix(in oklab, var(--pc, var(--p0)) 16%, transparent), transparent 70%),
    var(--surface-2);
  color: var(--text);
  box-shadow: var(--highlight-inset);
  transition: opacity var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out),
    transform var(--dur-3) var(--ease-spring);
}
[dir="rtl"] .pr-chip {
  background:
    linear-gradient(270deg, color-mix(in oklab, var(--pc, var(--p0)) 16%, transparent), transparent 70%),
    var(--surface-2);
}
.pr-chip__dot {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  inline-size: calc(var(--chip-h) - 10px);
  block-size: calc(var(--chip-h) - 10px);
  border-radius: 50%;
  background: var(--pc, var(--p0));
  color: var(--pc-ink, var(--ink-950));
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--pc, var(--p0)) 25%, transparent), 0 4px 14px -4px var(--pc, var(--p0));
  --icon-size: 58%;
}
.pr-chip__label {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: var(--fw-medium);
}
.pr-chip__score {
  font-family: var(--font-mono);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  padding-inline-start: var(--s-1);
}
.pr-chip__meta { color: var(--text-muted); font-size: var(--fs-sm); white-space: nowrap; }
.pr-chip__link {
  font-family: var(--font-mono);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .06em;
  padding: .15em .5em;
  border-radius: var(--r-pill);
  color: var(--text-muted);
  background: color-mix(in oklab, var(--ink-300) 10%, transparent);
  white-space: nowrap;
}
.pr-chip__link--p2p { color: var(--mint-400); background: color-mix(in oklab, var(--mint-500) 14%, transparent); }
[data-theme="light"] .pr-chip__link--p2p { color: var(--mint-600); }
.pr-chip--away { border-style: dashed; background: var(--surface); }
.pr-chip--away .pr-chip__label { color: var(--text-muted); }
.pr-chip--away .pr-chip__dot { background: color-mix(in oklab, var(--pc, var(--p0)) 45%, var(--surface-3)); box-shadow: none; }
.pr-chip--away .pr-chip__dot::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 2px solid transparent;
  border-block-start-color: var(--pc, var(--p0));
  animation: pr-spin 1.1s linear infinite;
}
.pr-chip--waiting { border-style: dashed; background: var(--surface); }
.pr-chip--waiting .pr-chip__label { color: var(--text-muted); }
.pr-chip--compact { --chip-h: 34px; gap: var(--s-1); padding-inline-end: var(--s-3); font-size: var(--fs-sm); }
.pr-chip--enter { animation: pr-pop var(--dur-3) var(--ease-spring) both; }

/* A bare player dot (no chip): .pr-dot.pr-p3 */
.pr-dot {
  display: inline-grid;
  place-items: center;
  inline-size: var(--dot-size, 28px);
  block-size: var(--dot-size, 28px);
  border-radius: 50%;
  background: var(--pc, var(--p0));
  color: var(--pc-ink, var(--ink-950));
  --icon-size: 58%;
}

/* ===========================================================================
 * QR panel  .pr-qr  — QR (SVG) on a white tile, join line, caption, live count
 *   (--compact: corner size; --live: animated light sweep around the tile)
 * ======================================================================== */
.pr-qr {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-4);
  text-align: center;
}
.pr-qr__tile {
  position: relative;
  flex: none;
  padding: var(--qr-pad, 14px);
  border-radius: var(--r-lg);
  background: #FFFFFF;
  box-shadow:
    0 0 0 1px rgb(255 255 255 / .6),
    0 0 0 8px color-mix(in oklab, var(--violet-500) 14%, transparent),
    0 24px 80px -24px color-mix(in oklab, var(--violet-500) 70%, transparent);
}
/* .pr-qr--live adds a slow light sweep around the tile (an always-on "live" hint for
 * pages whose main job is pairing, like the lobby). Decorative; off at the low tier. */
.pr-qr--live .pr-qr__tile::after {
  content: '';
  position: absolute;
  inset: -8px;
  border-radius: calc(var(--r-lg) + 8px);
  border: 2px solid transparent;
  background: conic-gradient(from var(--qr-angle, 0deg), transparent 0 70%, color-mix(in oklab, var(--mint-400) 85%, transparent) 85%, transparent 100%) border-box;
  -webkit-mask: linear-gradient(#000 0 0) padding-box exclude, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) padding-box exclude, linear-gradient(#000 0 0);
  animation: pr-qr-sweep 4.5s linear infinite;
  pointer-events: none;
}
.pr-qr__img {
  display: block;
  inline-size: var(--qr-size, 240px);
  block-size: var(--qr-size, 240px);
  image-rendering: pixelated;
}
/* Pending QR (.pr-qr--pending): no session yet (connecting, or the relay is unreachable)
 * or the session is stale (reconnecting). The tile keeps its size and shows a skeleton —
 * three ghost finder squares under a soft shimmer, with a spinner — instead of an image.
 * Never put a src-less <img> in the tile: Chromium draws its broken-image glyph and the
 * alt text. Markup:
 *   <div class="pr-qr__tile">
 *     <div class="pr-qr__skeleton" aria-hidden="true">
 *       <i class="pr-qr__finder"></i><i class="pr-qr__finder"></i><i class="pr-qr__finder"></i>
 *       <span class="pr-spinner pr-spinner--lg"></span>
 *     </div>
 *     <img class="pr-qr__img" hidden>   (src and alt are set together, then unhidden)
 *   </div>
 * With a stale QR both are shown: the image dims and the skeleton, with --overlay, lays
 * just its spinner over it. */
.pr-qr__tile { display: grid; }
.pr-qr__tile > * { grid-area: 1 / 1; }
.pr-qr__skeleton {
  --fs: calc(var(--qr-size, 240px) * .26);
  position: relative;
  display: grid;
  place-items: center;
  inline-size: var(--qr-size, 240px);
  block-size: var(--qr-size, 240px);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--ink-50);
}
.pr-qr__skeleton::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgb(255 255 255 / .85) 50%, transparent 70%);
  translate: -100% 0;
  animation: pr-qr-shimmer 1.6s var(--ease-out) infinite;
}
.pr-qr__finder {
  position: absolute;
  inline-size: var(--fs);
  block-size: var(--fs);
  border: calc(var(--fs) / 7) solid var(--ink-100);
  border-radius: calc(var(--fs) / 5);
}
.pr-qr__finder::after {
  content: '';
  position: absolute;
  inset: calc(var(--fs) / 7);
  border-radius: calc(var(--fs) / 10);
  background: var(--ink-100);
}
.pr-qr__finder:nth-child(1) { inset-block-start: 6%; inset-inline-start: 6%; }
.pr-qr__finder:nth-child(2) { inset-block-start: 6%; inset-inline-end: 6%; }
.pr-qr__finder:nth-child(3) { inset-block-end: 6%; inset-inline-start: 6%; }
.pr-qr__skeleton .pr-spinner { --spinner-c: var(--violet-500); position: relative; z-index: 1; }
.pr-qr--compact .pr-qr__skeleton .pr-spinner { --spinner-size: 1.5em; border-width: 2px; }
/* Stale QR: the old image stays (phones that already scanned it resume) but reads as paused. */
.pr-qr--pending .pr-qr__img { opacity: .14; filter: grayscale(1) blur(1px); transition: opacity var(--dur-3) var(--ease-out); }
.pr-qr__skeleton--overlay { background: transparent; }
.pr-qr__skeleton--overlay > .pr-qr__finder, .pr-qr__skeleton--overlay::after { display: none; }
.pr-qr--pending .pr-qr__tile { box-shadow: 0 0 0 1px rgb(255 255 255 / .35), 0 18px 60px -28px rgb(0 0 0 / .6); }
.pr-qr--pending .pr-qr__tile::after { display: none; }
html[data-quality="low"] .pr-qr__skeleton::after { display: none; }
.pr-qr__caption { color: var(--text-muted); font-size: var(--fs-md); }
.pr-qr__join {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: .4em .6em;
  font-size: var(--fs-lg);
  color: var(--text-muted);
}
.pr-qr__url { font-weight: var(--fw-medium); color: var(--text); direction: ltr; unicode-bidi: isolate; }
.pr-qr__sep { color: var(--text-faint); }
.pr-qr__code {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 1.35em;
  letter-spacing: .14em;
  color: var(--text);
  direction: ltr;
  unicode-bidi: isolate;
  padding: .05em .4em;
  border-radius: var(--r-sm);
  background: color-mix(in oklab, var(--violet-500) 18%, transparent);
  border: 1px solid color-mix(in oklab, var(--violet-400) 35%, transparent);
}
.pr-qr__count { display: inline-flex; align-items: center; gap: var(--s-2); color: var(--text-muted); font-size: var(--fs-sm); }
.pr-qr--compact { gap: var(--s-2); --qr-size: 120px; --qr-pad: 8px; }
.pr-qr--compact .pr-qr__tile {
  border-radius: var(--r-md);
  box-shadow: 0 0 0 1px rgb(255 255 255 / .6), 0 10px 40px -16px color-mix(in oklab, var(--violet-500) 70%, transparent);
}
.pr-qr--compact .pr-qr__join { font-size: var(--fs-sm); }

/* ===========================================================================
 * Connection pill  .pr-conn  (--connecting | --online | --p2p | --reconnecting | --offline)
 *   <span class="pr-conn pr-conn--online"><span class="pr-conn__dot"></span>Connected</span>
 * ======================================================================== */
.pr-conn {
  --conn-c: var(--ink-400);
  display: inline-flex;
  align-items: center;
  gap: .55em;
  min-block-size: 32px;
  padding: .3em .9em .3em .75em;
  border-radius: var(--r-pill);
  border: 1px solid color-mix(in oklab, var(--conn-c) 30%, transparent);
  background: color-mix(in oklab, var(--conn-c) 10%, var(--surface));
  color: var(--text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}
.pr-conn__dot {
  position: relative;
  inline-size: .6em;
  block-size: .6em;
  border-radius: 50%;
  background: var(--conn-c);
  flex: none;
}
.pr-conn__dot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--conn-c);
  opacity: 0;
}
.pr-conn--online, .pr-conn--p2p { --conn-c: var(--signal); }
.pr-conn--online .pr-conn__dot::after, .pr-conn--p2p .pr-conn__dot::after { animation: pr-ping 2.4s var(--ease-out) infinite; }
.pr-conn--connecting, .pr-conn--reconnecting { --conn-c: var(--warning); }
.pr-conn--connecting .pr-conn__dot, .pr-conn--reconnecting .pr-conn__dot { animation: pr-blink 1.1s ease-in-out infinite; }
.pr-conn--offline { --conn-c: var(--danger); }
.pr-conn__link { font-family: var(--font-mono); font-size: .85em; color: var(--mint-400); letter-spacing: .05em; }

/* ===========================================================================
 * Toast  .pr-toast  inside a region .pr-toasts (aria-live="polite")
 *   tones: --info (default) | --success | --warning | --danger
 * ======================================================================== */
.pr-toasts {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  pointer-events: none;
}
.pr-toast {
  --toast-c: var(--violet-400);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  max-inline-size: min(40rem, 92vw);
  padding: var(--s-3) var(--s-5) var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid color-mix(in oklab, var(--toast-c) 30%, var(--border));
  background:
    linear-gradient(90deg, color-mix(in oklab, var(--toast-c) 16%, transparent), transparent 55%),
    var(--surface-overlay);
  color: var(--text);
  box-shadow: var(--highlight-inset), var(--shadow-2);
  font-weight: var(--fw-medium);
  pointer-events: auto;
  animation: pr-toast-in var(--dur-3) var(--ease-spring) both;
}
[dir="rtl"] .pr-toast {
  background:
    linear-gradient(270deg, color-mix(in oklab, var(--toast-c) 16%, transparent), transparent 55%),
    var(--surface-overlay);
}
.pr-toast__icon { --icon-size: 1.3em; color: var(--toast-c); }
.pr-toast--success { --toast-c: var(--success); }
.pr-toast--warning { --toast-c: var(--warning); }
.pr-toast--danger { --toast-c: var(--danger); }
.pr-toast.is-leaving { animation: pr-toast-out var(--dur-2) var(--ease-in-out) both; }

/* ===========================================================================
 * Banner  .pr-banner  — a non-blocking status bar (connection loss, notices)
 * ======================================================================== */
.pr-banner {
  --banner-c: var(--warning);
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-2) var(--s-2) var(--s-4);
  min-block-size: 48px;
  border-radius: var(--r-pill);
  border: 1px solid color-mix(in oklab, var(--banner-c) 40%, transparent);
  background: color-mix(in oklab, var(--banner-c) 12%, var(--surface-overlay));
  color: var(--text);
  box-shadow: var(--shadow-2);
  font-weight: var(--fw-medium);
  animation: pr-drop-in var(--dur-3) var(--ease-spring) both;
}
.pr-banner:not(:has(.pr-btn)) { padding-inline-end: var(--s-5); }
.pr-banner__icon { --icon-size: 1.25em; color: var(--banner-c); }
.pr-banner--danger { --banner-c: var(--danger); }
.pr-banner--info { --banner-c: var(--violet-400); }
.pr-banner .pr-spinner { --spinner-c: var(--banner-c); }

/* ===========================================================================
 * Spinner / Pulse / Kbd
 * ======================================================================== */
.pr-spinner {
  --spinner-c: var(--accent-hover);
  display: inline-block;
  inline-size: var(--spinner-size, 1.25em);
  block-size: var(--spinner-size, 1.25em);
  border-radius: 50%;
  border: 2px solid color-mix(in oklab, var(--spinner-c) 22%, transparent);
  border-block-start-color: var(--spinner-c);
  animation: pr-spin .8s linear infinite;
  flex: none;
}
.pr-spinner--lg { --spinner-size: 2.5em; border-width: 3px; }
/* "Waiting for a phone": concentric signal rings around a core. */
.pr-pulse {
  --pulse-c: var(--signal);
  position: relative;
  display: inline-block;
  inline-size: var(--pulse-size, 14px);
  block-size: var(--pulse-size, 14px);
  border-radius: 50%;
  background: var(--pulse-c);
  box-shadow: 0 0 18px color-mix(in oklab, var(--pulse-c) 70%, transparent);
  flex: none;
}
.pr-pulse::before, .pr-pulse::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--pulse-c);
  animation: pr-ring 2.4s var(--ease-out) infinite;
}
.pr-pulse::after { animation-delay: 1.2s; }
.pr-kbd {
  display: inline-grid;
  place-items: center;
  min-inline-size: 1.9em;
  block-size: 1.9em;
  padding-inline: .45em;
  border-radius: 7px;
  border: 1px solid var(--border-strong);
  border-block-end-width: 3px;
  background: var(--surface-2);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: .8em;
  font-weight: 700;
  line-height: 1;
}

/* ===========================================================================
 * Logo  .pr-logo  — the mark plus the wordmark in Rubik 800 (§9.1)
 *   <a class="pr-logo" href="/"><img class="pr-logo__mark" src="/ds/brand/logo-mark.svg" alt="">
 *     <span class="pr-logo__word">PhoneRemo</span></a>
 * ======================================================================== */
.pr-logo {
  display: inline-flex;
  align-items: center;
  gap: .45em;
  color: var(--text);
  text-decoration: none;
  font-size: var(--logo-size, var(--fs-xl));
}
.pr-logo:hover { color: var(--text); }
.pr-logo__mark { inline-size: 1.45em; block-size: 1.45em; }
.pr-logo__word {
  font-weight: var(--fw-black);
  letter-spacing: -0.035em;
  line-height: 1;
  direction: ltr;
  unicode-bidi: isolate;
}
.pr-logo__word--he { direction: rtl; letter-spacing: -0.01em; }

/* ===========================================================================
 * Blocker  .pr-blocker  — a full-screen message that replaces the page
 * (the "this browser can't run 3D games" panel, §6.8). Also rendered by the
 * classic /gamekit/probe.js before any module runs, so it is kept simple.
 * ======================================================================== */
.pr-blocker {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  place-items: center;
  padding: 5vh 5vw;
  background:
    radial-gradient(50rem 30rem at 50% 0%, color-mix(in oklab, var(--violet-600) 22%, transparent), transparent 70%),
    var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  text-align: center;
}
.pr-blocker__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-5);
  max-inline-size: 42rem;
}
.pr-blocker__mark { inline-size: 5rem; block-size: 5rem; opacity: .95; }
.pr-blocker__title { font-size: var(--fs-3xl); font-weight: var(--fw-black); line-height: var(--lh-tight); letter-spacing: var(--tracking-display); }
.pr-blocker__text { font-size: var(--fs-lg); color: var(--text-muted); line-height: var(--lh-body); }
.pr-blocker__detail { font-size: var(--fs-sm); color: var(--text-muted); font-family: var(--font-mono); }

/* ===========================================================================
 * Game card  .pr-gamecard — poster, title, player range, control-type chip,
 * focus spotlight (lobby and landing).
 *   <a class="pr-gamecard" href="/games/bullseye/">
 *     <span class="pr-gamecard__poster"><img src="/games/bullseye/poster.webp" alt=""></span>
 *     <span class="pr-gamecard__body"><span class="pr-gamecard__title">Bullseye Tavern</span>
 *       <span class="pr-gamecard__meta"><span class="pr-gamecard__players">…1–8</span>
 *       <span class="pr-badge pr-gamecard__control">…Throw</span></span></span></a>
 * Without an image the poster shows a gradient in `--gc` (the game's color).
 * ======================================================================== */
.pr-gamecard {
  --gc: var(--violet-500);
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  box-shadow: var(--highlight-inset), var(--shadow-1);
  cursor: pointer;
  isolation: isolate;
  transition: transform var(--dur-3) var(--ease-spring), box-shadow var(--dur-2) var(--ease-out),
    border-color var(--dur-2) var(--ease-out);
}
.pr-gamecard__poster {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 20% 0%, color-mix(in oklab, var(--gc) 70%, transparent), transparent 60%),
    linear-gradient(160deg, color-mix(in oklab, var(--gc) 40%, var(--ink-900)), var(--ink-950));
}
.pr-gamecard__poster img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform var(--dur-4) var(--ease-out);
}
/* The spotlight: a soft light band that sweeps across the poster on focus. */
.pr-gamecard__poster::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 35%, rgb(255 255 255 / .16) 50%, transparent 65%);
  translate: -110% 0;
  pointer-events: none;
}
.pr-gamecard__body { display: flex; flex-direction: column; gap: var(--s-2); padding: var(--s-4) var(--s-5) var(--s-5); }
.pr-gamecard__title { font-size: var(--fs-xl); font-weight: var(--fw-bold); line-height: var(--lh-snug); }
.pr-gamecard__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3); color: var(--text-muted); font-size: var(--fs-sm); }
.pr-gamecard__players { display: inline-flex; align-items: center; gap: var(--s-1); --icon-size: 1.15em; }
.pr-gamecard__control { --icon-size: 1.1em; }
.pr-gamecard:hover,
.pr-gamecard:focus-visible,
.pr-gamecard.is-focused {
  transform: translateY(-6px) scale(var(--focus-scale));
  border-color: color-mix(in oklab, var(--gc) 60%, transparent);
  box-shadow: var(--highlight-inset), 0 0 0 1px color-mix(in oklab, var(--gc) 40%, transparent),
    0 18px 60px -18px color-mix(in oklab, var(--gc) 70%, transparent);
  outline: none;
}
.pr-gamecard:focus-visible { outline: var(--ring-width) solid var(--focus); outline-offset: var(--ring-offset); }
.pr-gamecard:is(:hover, :focus-visible, .is-focused) .pr-gamecard__poster img { transform: scale(1.05); }
.pr-gamecard:is(:focus-visible, .is-focused) .pr-gamecard__poster::after { animation: pr-spotlight 1.1s var(--ease-out) both; }
.pr-gamecard:active { transform: translateY(-2px) scale(.99); }
[dir="rtl"] .pr-gamecard__poster::after { background: linear-gradient(-105deg, transparent 35%, rgb(255 255 255 / .16) 50%, transparent 65%); translate: 110% 0; }
[dir="rtl"] .pr-gamecard:is(:focus-visible, .is-focused) .pr-gamecard__poster::after { animation-name: pr-spotlight-rtl; }
@keyframes pr-spotlight { from { translate: -110% 0; } to { translate: 110% 0; } }
@keyframes pr-spotlight-rtl { from { translate: 110% 0; } to { translate: -110% 0; } }

/* ===========================================================================
 * Dialog / Sheet  dialog.pr-dialog, dialog.pr-sheet
 * Native <dialog> opened with showModal(): the browser traps focus, makes
 * the page inert and closes on Escape. `.pr-dialog__head` (title, close),
 * `.pr-dialog__body`, `.pr-dialog__actions`. The sheet is the phone's bottom
 * sheet with a grab handle.
 * ======================================================================== */
.pr-dialog, .pr-sheet {
  padding: 0;
  border: 1px solid var(--border);
  background: var(--surface-overlay);
  color: var(--text);
  box-shadow: var(--highlight-inset), var(--shadow-2);
}
.pr-dialog {
  inline-size: min(34rem, calc(100vw - 2 * var(--s-5)));
  max-block-size: calc(100dvh - 2 * var(--s-6));
  border-radius: var(--r-xl);
}
.pr-dialog::backdrop, .pr-sheet::backdrop { background: var(--scrim); }
.pr-dialog[open] { animation: pr-dialog-in var(--dur-3) var(--ease-spring) both; }
.pr-dialog[open]::backdrop, .pr-sheet[open]::backdrop { animation: pr-fade-in var(--dur-2) var(--ease-out) both; }
.pr-dialog__head { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-5) var(--s-5) 0 var(--s-6); }
.pr-dialog__title { flex: 1; font-size: var(--fs-xl); font-weight: var(--fw-bold); line-height: var(--lh-snug); }
.pr-dialog__body { padding: var(--s-4) var(--s-6); color: var(--text-muted); line-height: var(--lh-body); }
.pr-dialog__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--s-3); padding: 0 var(--s-6) var(--s-6); }
.pr-sheet {
  inline-size: 100%;
  max-inline-size: 40rem;
  max-block-size: 85dvh;
  margin: auto auto 0;
  border-block-end: 0;
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding-block-end: env(safe-area-inset-bottom, 0px);
}
.pr-sheet::before {
  content: '';
  display: block;
  inline-size: 40px;
  block-size: 5px;
  margin: var(--s-3) auto 0;
  border-radius: var(--r-pill);
  background: var(--ink-500);
}
.pr-sheet[open] { animation: pr-sheet-in var(--dur-3) var(--ease-out) both; }
@keyframes pr-dialog-in { from { transform: translateY(10px) scale(.96); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes pr-sheet-in { from { transform: translateY(100%); } to { transform: none; } }
@keyframes pr-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* ===========================================================================
 * Segmented control / Tabs  .pr-seg > button.pr-seg__item
 * Selected: aria-pressed="true", aria-selected="true" (tabs) or aria-checked="true" (radios).
 * ======================================================================== */
.pr-seg {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--r-pill);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: inset 0 1px 2px rgb(0 0 0 / .3);
}
.pr-seg__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-block-size: max(38px, var(--target-min));
  padding-inline: var(--s-4);
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-weight: var(--fw-medium);
  font-size: var(--fs-sm);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease-out), color var(--dur-1) var(--ease-out);
  --icon-size: 1.1em;
}
.pr-seg__item:hover { color: var(--text); }
.pr-seg__item:is([aria-pressed="true"], [aria-selected="true"], [aria-checked="true"]) {
  background: var(--surface-3);
  color: var(--text);
  box-shadow: var(--highlight-inset), 0 1px 3px rgb(0 0 0 / .35);
}
.pr-seg__item:focus-visible { outline: var(--ring-width) solid var(--focus); outline-offset: 1px; }
.pr-seg--block { display: flex; }
.pr-seg--block .pr-seg__item { flex: 1; }
body.tv .pr-seg__item { min-block-size: max(var(--target-min), 2.4rem); font-size: var(--fs-md); }

/* ===========================================================================
 * Toggle  label.pr-toggle > input[type=checkbox][role=switch] + .pr-toggle__track + text
 * Works without JavaScript (the input is the control).
 * ======================================================================== */
.pr-toggle { display: inline-flex; align-items: center; gap: var(--s-3); min-block-size: var(--target-min); cursor: pointer; user-select: none; }
.pr-toggle > input { position: absolute; opacity: 0; inline-size: 1px; block-size: 1px; pointer-events: none; }
.pr-toggle__track {
  position: relative;
  flex: none;
  inline-size: 48px;
  block-size: 28px;
  border-radius: var(--r-pill);
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  transition: background-color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out);
}
.pr-toggle__track::after {
  content: '';
  position: absolute;
  inset-block-start: 3px;
  inset-inline-start: 3px;
  inline-size: 20px;
  block-size: 20px;
  border-radius: 50%;
  background: var(--ink-200);
  box-shadow: 0 1px 3px rgb(0 0 0 / .4);
  transition: transform var(--dur-3) var(--ease-spring), background-color var(--dur-2) var(--ease-out);
}
.pr-toggle > input:checked + .pr-toggle__track { background: var(--accent); border-color: transparent; }
.pr-toggle > input:checked + .pr-toggle__track::after { background: #FFFFFF; transform: translateX(20px); }
[dir="rtl"] .pr-toggle > input:checked + .pr-toggle__track::after { transform: translateX(-20px); }
.pr-toggle > input:focus-visible + .pr-toggle__track { outline: var(--ring-width) solid var(--focus); outline-offset: 2px; }
.pr-toggle > input:disabled + .pr-toggle__track { opacity: .45; }

/* ===========================================================================
 * Slider  input[type=range].pr-slider — the filled part follows `--pct`
 * (set by ds/components.js; without it the track is plain).
 * ======================================================================== */
.pr-slider {
  --pct: 50%;
  --fill: var(--accent);
  appearance: none;
  inline-size: 100%;
  block-size: max(28px, var(--target-min));
  background: transparent;
  cursor: pointer;
}
.pr-slider::-webkit-slider-runnable-track {
  block-size: 6px;
  border-radius: var(--r-pill);
  background: linear-gradient(to right, var(--fill) var(--pct), var(--surface-3) var(--pct));
}
[dir="rtl"] .pr-slider::-webkit-slider-runnable-track { background: linear-gradient(to left, var(--fill) var(--pct), var(--surface-3) var(--pct)); }
.pr-slider::-moz-range-track { block-size: 6px; border-radius: var(--r-pill); background: var(--surface-3); }
.pr-slider::-moz-range-progress { block-size: 6px; border-radius: var(--r-pill); background: var(--fill); }
.pr-slider::-webkit-slider-thumb {
  appearance: none;
  inline-size: 22px;
  block-size: 22px;
  margin-block-start: -8px;
  border-radius: 50%;
  background: #FFFFFF;
  border: 0;
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 35%, transparent), 0 2px 6px rgb(0 0 0 / .45);
  transition: transform var(--dur-2) var(--ease-spring);
}
.pr-slider::-moz-range-thumb {
  inline-size: 22px;
  block-size: 22px;
  border-radius: 50%;
  background: #FFFFFF;
  border: 0;
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 35%, transparent), 0 2px 6px rgb(0 0 0 / .45);
}
.pr-slider:active::-webkit-slider-thumb { transform: scale(1.12); }
.pr-slider:focus-visible { outline: var(--ring-width) solid var(--focus); outline-offset: 4px; border-radius: var(--r-sm); }

/* ===========================================================================
 * Language switch  nav.pr-lang > a[lang] (aria-current="true" on the active one)
 * ======================================================================== */
.pr-lang {
  display: inline-flex;
  padding: 3px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  background: var(--surface);
}
.pr-lang > a, .pr-lang > button {
  display: inline-grid;
  place-items: center;
  min-inline-size: max(2.6em, var(--target-min));
  min-block-size: max(32px, var(--target-min));
  padding-inline: var(--s-2);
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  text-decoration: none;
  cursor: pointer;
}
.pr-lang > :hover { color: var(--text); }
.pr-lang > [aria-current="true"], .pr-lang > [aria-pressed="true"] { background: var(--surface-3); color: var(--text); box-shadow: var(--highlight-inset); }
.pr-lang > :focus-visible { outline: var(--ring-width) solid var(--focus); outline-offset: 1px; }

/* ===========================================================================
 * Code block  .pr-code > pre > code, button.pr-code__copy (ds/components.js copies)
 * Always left-to-right, even on Hebrew pages.
 * ======================================================================== */
.pr-code {
  position: relative;
  direction: ltr;
  text-align: left;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--ink-950);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .03);
}
.pr-code pre {
  margin: 0;
  padding: var(--s-4) var(--s-5);
  padding-inline-end: calc(var(--s-5) + 44px);
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--ink-100);
  tab-size: 2;
}
.pr-code__copy { position: absolute; inset-block-start: var(--s-2); right: var(--s-2); --ib-size: 36px; --icon-size: 18px; }
.pr-code__copy.is-done { color: var(--signal); }
.pr-code__label {
  display: block;
  padding: var(--s-2) var(--s-5) 0;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* ===========================================================================
 * Header / Footer  .pr-header, .pr-footer (landing, lobby, legal)
 * ======================================================================== */
.pr-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-overlay);
  display: flex;
  align-items: center;
  gap: var(--s-5);
  min-block-size: 72px;
  padding-inline: var(--s-6);
  background: color-mix(in oklab, var(--bg) 82%, transparent);
  border-block-end: 1px solid var(--border);
}
.pr-header__nav { display: flex; align-items: center; gap: var(--s-1); margin-inline-start: auto; }
.pr-header__link {
  display: inline-flex;
  align-items: center;
  min-block-size: max(40px, var(--target-min));
  padding-inline: var(--s-3);
  border-radius: var(--r-pill);
  color: var(--text-muted);
  text-decoration: none;
  font-weight: var(--fw-medium);
}
.pr-header__link:hover, .pr-header__link[aria-current="page"] { color: var(--text); background: color-mix(in oklab, var(--ink-300) 8%, transparent); }
.pr-header__actions { display: flex; align-items: center; gap: var(--s-3); }
.pr-footer {
  display: grid;
  gap: var(--s-6);
  padding: var(--s-8) var(--s-6) var(--s-7);
  border-block-start: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.pr-footer__cols { display: grid; gap: var(--s-6); grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); }
.pr-footer__title { margin-block-end: var(--s-3); color: var(--text); font-weight: var(--fw-bold); font-size: var(--fs-md); }
.pr-footer__links { display: grid; gap: var(--s-2); margin: 0; padding: 0; list-style: none; }
.pr-footer a { color: var(--text-muted); text-decoration: none; }
.pr-footer a:hover { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.pr-footer__legal { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s-3); padding-block-start: var(--s-5); border-block-start: 1px solid var(--border); }
@media (max-width: 640px) { .pr-header { padding-inline: var(--s-4); gap: var(--s-3); } .pr-header__nav { display: none; } }

/* ===========================================================================
 * Phone controls (the controller's building blocks, §9.5)
 *   .pr-pad      touch surface (pointer trackpad, throw swipe area); .is-active while touched
 *   .pr-dpad     3×3 D-pad: .pr-dpad__btn--up/--down/--left/--right/--ok
 *   .pr-stick    virtual stick: .pr-stick__knob follows --x / --y (-1..1)
 *   .pr-trigger  tall pedal (gas, brake): fill from --level (0..1)
 *   .pr-grip     side grip zone (held = .is-active)
 *   .pr-hold     hold-to-confirm ring: --hold (0..1), or .is-holding animates over --hold-ms
 *   .pr-statusbar  top bar: connection, player, menu
 *   .pr-hudstrip   the screen's HUD on the phone: title, text, score, progress (--progress)
 * Pressed states come from phone.css (.is-pressed + haptics).
 * ======================================================================== */
.pr-pad {
  position: relative;
  display: grid;
  place-items: center;
  min-block-size: 12rem;
  border-radius: var(--r-xl);
  border: 1px solid var(--border);
  background:
    radial-gradient(circle at 50% 50%, color-mix(in oklab, var(--violet-500) 10%, transparent), transparent 70%),
    radial-gradient(circle, color-mix(in oklab, var(--ink-300) 14%, transparent) 1.2px, transparent 1.6px) 0 0 / 22px 22px,
    var(--surface);
  color: var(--text-muted);
  touch-action: none;
  transition: border-color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.pr-pad.is-active { border-color: color-mix(in oklab, var(--violet-400) 55%, transparent); box-shadow: inset 0 0 40px -10px color-mix(in oklab, var(--violet-500) 60%, transparent); }
.pr-pad__hint { font-size: var(--fs-sm); pointer-events: none; }

.pr-dpad {
  --dp: max(64px, var(--target-min));
  display: grid;
  grid-template-columns: repeat(3, var(--dp));
  grid-template-rows: repeat(3, var(--dp));
  gap: 4px;
  direction: ltr; /* a D-pad is physical: left is left in every language */
}
.pr-dpad__btn {
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
  box-shadow: var(--highlight-inset);
  --icon-size: 28px;
  cursor: pointer;
}
.pr-dpad__btn--up { grid-area: 1 / 2; border-radius: var(--r-md) var(--r-md) var(--r-sm) var(--r-sm); }
.pr-dpad__btn--left { grid-area: 2 / 1; border-radius: var(--r-md) var(--r-sm) var(--r-sm) var(--r-md); }
.pr-dpad__btn--right { grid-area: 2 / 3; border-radius: var(--r-sm) var(--r-md) var(--r-md) var(--r-sm); }
.pr-dpad__btn--down { grid-area: 3 / 2; border-radius: var(--r-sm) var(--r-sm) var(--r-md) var(--r-md); }
.pr-dpad__btn--ok { grid-area: 2 / 2; border-radius: 50%; background: var(--gradient-accent); color: var(--on-accent); font-weight: var(--fw-bold); }
.pr-dpad__btn:focus-visible { outline: var(--ring-width) solid var(--focus); outline-offset: 1px; }

.pr-stick {
  --size: 9rem;
  --x: 0;
  --y: 0;
  position: relative;
  inline-size: var(--size);
  block-size: var(--size);
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: radial-gradient(circle, var(--surface-2) 0 55%, var(--surface) 56%);
  box-shadow: inset 0 2px 10px rgb(0 0 0 / .45);
  touch-action: none;
  direction: ltr;
}
.pr-stick__knob {
  position: absolute;
  inset: 0;
  margin: auto;
  inline-size: 42%;
  block-size: 42%;
  border-radius: 50%;
  background: var(--gradient-accent);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .25), 0 6px 16px -4px rgb(0 0 0 / .6);
  transform: translate(calc(var(--x) * var(--size) * .29), calc(var(--y) * var(--size) * -.29));
}
.pr-stick.is-active .pr-stick__knob { box-shadow: inset 0 1px 0 rgb(255 255 255 / .25), var(--glow-accent); }

.pr-trigger {
  --level: 0;
  --trig-c: var(--signal);
  position: relative;
  display: grid;
  place-items: end center;
  inline-size: 5.5rem;
  min-block-size: 11rem;
  padding-block-end: var(--s-4);
  overflow: hidden;
  border-radius: var(--r-lg);
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--text);
  font-weight: var(--fw-bold);
  touch-action: none;
  cursor: pointer;
  --icon-size: 30px;
}
.pr-trigger::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, color-mix(in oklab, var(--trig-c) 55%, transparent), color-mix(in oklab, var(--trig-c) 15%, transparent));
  transform-origin: bottom;
  transform: scaleY(var(--level));
  transition: transform var(--dur-1) linear;
}
.pr-trigger > * { position: relative; }
.pr-trigger--brake { --trig-c: var(--danger); }

.pr-grip {
  inline-size: 3.5rem;
  min-block-size: 12rem;
  border-radius: var(--r-xl);
  border: 1px dashed var(--border-strong);
  background: repeating-linear-gradient(0deg, transparent 0 10px, color-mix(in oklab, var(--ink-300) 8%, transparent) 10px 12px), var(--surface);
  touch-action: none;
}
.pr-grip.is-active { border-style: solid; border-color: var(--signal); box-shadow: var(--glow-signal); }

.pr-hold {
  --hold: 0;
  --hold-ms: 900ms;
  position: relative;
  display: inline-grid;
  place-items: center;
  inline-size: 5rem;
  block-size: 5rem;
  border: 0;
  border-radius: 50%;
  background: conic-gradient(var(--danger) calc(var(--hold) * 360deg), var(--surface-3) 0);
  color: var(--text);
  cursor: pointer;
  --icon-size: 28px;
}
.pr-hold::before { content: ''; position: absolute; inset: 5px; border-radius: 50%; background: var(--surface-2); box-shadow: var(--highlight-inset); }
.pr-hold > * { position: relative; }
.pr-hold.is-holding { animation: pr-hold var(--hold-ms) linear forwards; }
@property --hold { syntax: '<number>'; inherits: true; initial-value: 0; }
@keyframes pr-hold { from { --hold: 0; } to { --hold: 1; } }
/* The hold time is a safety timer, not decoration: it keeps its length under reduced motion. */
@media (prefers-reduced-motion: reduce) { .pr-hold.is-holding { animation-duration: var(--hold-ms) !important; } }

.pr-statusbar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-block-size: 52px;
  padding-inline: var(--s-3);
  border-block-end: 1px solid var(--border);
  background: color-mix(in oklab, var(--bg) 90%, transparent);
}
.pr-statusbar__spacer { flex: 1; }
.pr-statusbar .pr-chip { --chip-h: 34px; font-size: var(--fs-sm); }

.pr-hudstrip {
  --progress: 0;
  --hud-c: var(--accent);
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 0 var(--s-3);
  padding: var(--s-2) var(--s-4) var(--s-3);
  overflow: hidden;
  border-radius: var(--r-md);
  border: 1px solid color-mix(in oklab, var(--hud-c) 30%, transparent);
  background: color-mix(in oklab, var(--hud-c) 10%, var(--surface));
}
.pr-hudstrip__title { font-size: var(--fs-xs); color: var(--text-muted); font-weight: var(--fw-medium); }
.pr-hudstrip__text { grid-column: 1; font-weight: var(--fw-medium); }
.pr-hudstrip__score { grid-row: 1 / span 2; grid-column: 2; font-size: var(--fs-2xl); font-weight: var(--fw-black); font-variant-numeric: tabular-nums; }
.pr-hudstrip::after {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  inset-block-end: 0;
  block-size: 3px;
  inline-size: calc(var(--progress) * 100%);
  background: var(--hud-c);
  transition: inline-size var(--dur-2) var(--ease-out);
}

/* ===========================================================================
 * Keyframes
 * ======================================================================== */
@keyframes pr-spin { to { transform: rotate(360deg); } }
@keyframes pr-blink { 50% { opacity: .35; } }
@keyframes pr-ping { 0% { transform: scale(1); opacity: .55; } 80%, 100% { transform: scale(3.2); opacity: 0; } }
@keyframes pr-ring { 0% { transform: scale(1); opacity: .7; } 100% { transform: scale(3.4); opacity: 0; } }
@keyframes pr-pop { 0% { transform: scale(.6); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
@keyframes pr-toast-in { 0% { transform: translateY(16px) scale(.96); opacity: 0; } 100% { transform: none; opacity: 1; } }
@keyframes pr-toast-out { to { transform: translateY(8px) scale(.98); opacity: 0; } }
@keyframes pr-drop-in { 0% { transform: translateY(-14px); opacity: 0; } 100% { transform: none; opacity: 1; } }
@property --qr-angle { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
@keyframes pr-qr-sweep { to { --qr-angle: 360deg; } }
@keyframes pr-qr-shimmer { to { translate: 100% 0; } }
