/* ============================================================================
 * THE CONSOLE — components and shell for the host console (2026-09-07).
 *
 * Reads room.css and nothing else. Five layers, in this order:
 *   reset       the page's ground rules
 *   components  the pp-* vocabulary: one button family, the ink switch, tiles,
 *               readouts, posters, rows, sheets, the notice
 *   shell       the HUD, the rail, the wordmark's fold and misregister
 *   compat      the OLD console's class names, mapped onto the new tokens so a
 *               page that has not been ported yet still renders. Each rule
 *               names its consumers; delete it when its last consumer is ported.
 *   page        reserved for a page's own hoisted <style>
 *
 * The rules of the room, enforced here rather than remembered:
 *   - no gradient on a surface, no glow, no blur for mood, no bevel/hairlight
 *   - state is brightness, fill and shape, never hue; red means error only
 *   - a mark over content the app does not control is a bare glyph + rail shadow
 *   - every figure is tabular and every figure ROLLS (shell.js PPHost.roll)
 *   - anything moving animates transform/opacity only; reduce-motion keeps the
 *     STATE and drops the travel (room.css collapses every duration)
 * The order is the order the @layer blocks first appear in below, which is what
 * the cascade uses; an explicit `@layer a, b, c;` statement would say the same
 * thing and scripts/oracle/css-oracle.sh cannot parse one.
 * ========================================================================== */

/* ============================================================ reset ======= */
@layer reset {
  *, *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
  html { overflow-x: hidden; -webkit-text-size-adjust: 100%; }
  html, body { margin: 0; }
  body {
    min-height: 100vh; min-height: 100dvh;
    background: var(--void); color: var(--text);
    font-family: var(--program); font-size: var(--p-body); line-height: 1.5;
    -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
  }
  /* The room IS the ground on a coloured way: the canvas paints it, and the page
   * behind the canvas matches so an unpainted frame never shows the void. */
  :root[data-way="pink"] body, :root[data-way="blue"] body { background: var(--ground); }
  a { color: var(--text); text-decoration-thickness: 1px; text-underline-offset: .18em; }
  h1, h2, h3, h4 { margin: 0; font-weight: 500; }
  p { margin: 0; }
  button, input, select, textarea { font: inherit; color: inherit; }
  img, svg, video, canvas { display: block; max-width: 100%; }
  /* Every figure in the app is tabular, so a count that changes does not reflow its line. */
  [data-figure], .pp-nums, .ts-nums { font-variant-numeric: tabular-nums; }
  :focus-visible { outline: 2px solid var(--catch); outline-offset: 3px; }
  ::selection { background: var(--catch-wash); }
}

/* ======================================================= components ======= */
@layer components {

  /* ---- type ------------------------------------------------------------ */
  .pp-marquee { font-family: var(--marquee); font-weight: 500; }
  .pp-kicker, label.pp-kicker {
    display: block; font-family: var(--marquee); font-weight: 500;
    font-size: var(--t-kicker); letter-spacing: var(--trk-kicker); text-transform: uppercase;
    color: var(--text-dim); line-height: 1.2;
  }
  .pp-eyebrow {
    font-family: var(--marquee); font-weight: 500; font-size: var(--t-eyebrow);
    letter-spacing: var(--trk-eyebrow); text-transform: uppercase; color: var(--text-faint); line-height: 1.2;
  }
  .pp-title { font-family: var(--marquee); font-weight: 500; font-size: var(--t-name); line-height: 1.05; letter-spacing: .005em; color: var(--text); }
  .pp-page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; margin: var(--s-6) 0 var(--s-5); }
  .pp-page-head h1 {
    font-family: var(--marquee); font-weight: 500; font-size: var(--t-room); line-height: 1.1;
    letter-spacing: var(--trk-room); text-transform: uppercase; color: var(--text);
  }
  .pp-page-head p { color: var(--text-dim); font-size: var(--p-body); margin-top: var(--s-1); }
  .pp-section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); margin: var(--s-6) 0 var(--s-3); }
  .pp-section-head h2 {
    font-family: var(--marquee); font-weight: 500; font-size: var(--t-section); line-height: 1.1;
    letter-spacing: var(--trk-section); text-transform: uppercase; color: var(--text);
  }
  .pp-section-head p { color: var(--text-dim); font-size: 14px; }
  .pp-dim { color: var(--text-dim); }
  .pp-faint { color: var(--text-faint); }
  .pp-hint { font-size: var(--p-hint); color: var(--text-dim); line-height: 1.45; }
  .pp-foot { font-size: var(--p-foot); color: var(--text-faint); line-height: 1.45; }
  .pp-error-line { font-size: var(--p-hint); color: var(--error); line-height: 1.45; }
  .pp-error-line:empty { display: none; }

  /* ---- the press. One treatment for every tappable: a little smaller, a little
   * dimmer, 120ms. Under reduced motion the scale goes and the DIM STAYS: a press
   * has to register, eyes-on, when the travel is gone. */
  .pp-press { transition: transform var(--m-fast) var(--ease-house), opacity var(--m-fast) var(--ease-house); }
  .pp-press:active { transform: scale(.97); opacity: .72; }
  .pp-tile.pp-press:active { transform: scale(.96); }
  @media (prefers-reduced-motion: reduce) { .pp-press:active { transform: none; } }

  /* ---- the button. ONE family, and the KEY is an object under a light.
   * Round three (2026-09-07), after reading the app's own controls: the key is the app's
   * showcoat (Showcoat.swift), never flat — a three-stop vertical coat, a travelling
   * specular pool, a counter-moving lowlight, a 1px hairlight on top and a 1px shade seam
   * at the foot, drawn by the coat itself so no stroke is stacked on it. The phone lights
   * it with the gyro; the web lights it with the pointer (shell.js writes --gx/--gy).
   * The press dims the PAINT from inside (not the object going translucent), scales the
   * object .98, and throws the label and glyph into their two ink passes, the icon's own
   * construction; release springs them back into register (Misregister.swift).
   *   --key    the one filled, pressable face on a screen; dark type; a cast shadow
   *   --ghost  clear, with the hairline that is the only thing telling it from a label;
   *            the light still moves, along the top edge
   *   --danger red type and red edge; the press floods it. Red means only this.
   *   --link   bare words with a 44px target; does not sink. */
  .pp-btn {
    --gx: 50%; --gy: 26%; --misreg: 0;
    position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 9px;
    min-height: var(--ctl-md); padding: 0 22px; border-radius: var(--r-ctl); isolation: isolate;
    border: 1px solid transparent; background: transparent; cursor: pointer;
    font-family: var(--marquee); font-weight: 500; font-size: 17px; letter-spacing: var(--trk-cta);
    line-height: 1; white-space: nowrap; text-decoration: none; color: var(--text);
    transition: transform var(--spring-seat-dur) var(--spring-seat), border-color var(--m-base) var(--ease-house),
      background var(--m-base) var(--ease-house), color var(--m-base) var(--ease-house), box-shadow var(--m-fast) var(--ease-house);
  }
  .pp-btn:active, .pp-btn.is-down { transform: scale(.97); transition: transform 90ms var(--ease-house); }
  .pp-btn--sm { min-height: var(--ctl-sm); padding: 0 16px; font-size: var(--t-cta); }
  .pp-btn--lg { min-height: var(--ctl-lg); padding: 0 28px; font-size: 19px; }
  .pp-btn--capsule { border-radius: 999px; }
  .pp-btn svg:not(.pp-glyph-svg):not(.pp-chev) { width: 1.1em; height: 1.1em; stroke: currentColor; fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
  .pp-btn .pp-glyph { width: 1.15em; height: 1.15em; }
  /* The chevron on anything that advances: 12px, bold, at 70% of the label, trailing. */
  .pp-chev { display: inline-block; width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; opacity: .7; margin-left: 2px; flex: none; }
  .pp-chev > svg { width: 100%; height: 100%; display: block; stroke-width: 3; }

  /* The passes. shell.js prints each button's content three times inside .pp-ink: pass A,
   * pass B, then the face. At --misreg 0 they sit exactly under the face. */
  .pp-ink { position: relative; display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
  .pp-pass { position: absolute; inset: 0; display: inline-flex; align-items: center; gap: 9px; pointer-events: none; transition: transform var(--misreg-dur, 180ms) var(--misreg-ease, var(--ease-house)); }
  .pp-pass-a { color: var(--pass-a); transform: translate(calc(-1px * var(--misreg)), calc(-.7px * var(--misreg))); }
  .pp-pass-b { color: var(--pass-b); transform: translate(calc(1px * var(--misreg)), calc(.7px * var(--misreg))); }
  .pp-face { position: relative; display: inline-flex; align-items: center; gap: 9px; }
  .pp-btn--capsule .pp-ink { justify-content: center; }
  .pp-btn[data-full] { width: 100%; }
  .pp-btn[data-full] .pp-ink { justify-content: center; }

  .pp-btn--key {
    color: var(--on-key); border: 0; overflow: hidden;
    background:
      radial-gradient(240px circle at calc(100% - var(--gx)) calc(105% - var(--gy)), rgba(42,42,48,.20), transparent 60%),
      radial-gradient(220px circle at var(--gx) var(--gy), rgba(255,255,255,.55), rgba(255,255,255,.18) 26%, transparent 54%),
      linear-gradient(180deg, #FFFFFF 0%, #F0F0F2 48%, #C6C6CC 100%);
    box-shadow: var(--sh-key);   /* a cast shadow, not a bloom: it sits on the panel like the object it is */
  }
  .pp-btn--key::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; background: rgba(255,255,255,.30); pointer-events: none; z-index: 1; }
  .pp-btn--key::after  { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: rgba(42,42,48,.40); pointer-events: none; z-index: 1; }
  .pp-btn--key:active, .pp-btn--key.is-down { transform: scale(.98); box-shadow: var(--sh-key), inset 0 0 0 100vmax rgba(0,0,0,.18); }
  .pp-btn--key:hover { border: 0; }

  .pp-btn--ghost { border-color: rgba(255, 255, 255, .07); }
  .pp-btn--ghost::before, .pp-btn--danger::before {
    content: ""; position: absolute; left: 8px; right: 8px; top: -1px; height: 1px; pointer-events: none; opacity: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.45) var(--gx), transparent); transition: opacity var(--m-base) var(--ease-house);
  }
  .pp-btn--ghost:hover::before, .pp-btn--danger:hover::before { opacity: 1; }
  .pp-btn--ghost:active, .pp-btn--ghost.is-down, .pp-btn--ghost[aria-pressed="true"] { border-color: var(--catch-line); }
  .pp-btn--ghost[aria-pressed="true"] { background: var(--catch-wash); }

  .pp-btn--danger { color: var(--error); border-color: rgba(255, 77, 77, .45); }
  .pp-btn--danger::before { background: linear-gradient(90deg, transparent, rgba(255,120,120,.55) var(--gx), transparent); }
  .pp-btn--danger:active, .pp-btn--danger.is-down { background: var(--error); color: #1A0507; border-color: var(--error); }

  .pp-btn--link { min-height: var(--ctl-sm); padding: 0 4px; border-radius: var(--r-xs); color: var(--text-dim); font-size: var(--t-cta); }
  .pp-btn--link:hover, .pp-btn--link:active { color: var(--text); }
  .pp-btn--link:active, .pp-btn--link.is-down { transform: none; }

  .pp-btn:disabled, .pp-btn[aria-disabled="true"] { opacity: .45; cursor: default; pointer-events: none; }
  /* Loading never resizes and never swaps identity: the face goes to 0 and a spinner sits on it. */
  .pp-btn.is-loading { pointer-events: none; }
  .pp-btn.is-loading .pp-ink > * { opacity: 0; }
  .pp-btn.is-loading .pp-ink::after { content: ""; position: absolute; left: calc(50% - 8px); top: calc(50% - 8px); width: 16px; height: 16px; border-radius: 50%; border: 2px solid currentColor; border-top-color: transparent; animation: pp-spin .7s linear infinite; }
  @keyframes pp-spin { to { transform: rotate(360deg); } }
  @media (prefers-reduced-motion: reduce) { .pp-btn:active, .pp-btn.is-down, .pp-btn--key:active, .pp-btn--key.is-down { transform: none; } }
  .pp-actions { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }

  /* ---- fields ------------------------------------------------------------ */
  .pp-field {
    display: block; width: 100%; min-height: var(--ctl-md); padding: 12px 14px;
    background: var(--raised); color: var(--text); border: 1px solid var(--edge); border-radius: var(--r-ctl);
    font-family: var(--program); font-size: 16px; line-height: 1.3; outline: none; caret-color: var(--catch);
    transition: border-color var(--m-base) var(--ease-house), box-shadow var(--m-base) var(--ease-house);
  }
  .pp-field:focus { border-color: var(--catch-line); box-shadow: 0 0 0 3px var(--catch-wash); }
  .pp-field::placeholder { color: var(--text-faint); }
  select.pp-field {
    appearance: none; -webkit-appearance: none; padding-right: 38px;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239E9EA6' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>");
    background-repeat: no-repeat; background-position: right 12px center; background-size: 16px;
  }
  textarea.pp-field { resize: vertical; min-height: 96px; }
  .pp-field-row { display: flex; gap: var(--s-3); align-items: flex-end; }
  .pp-field-row > div { flex: 1; min-width: 0; }
  .pp-field-row .pp-kicker { margin-bottom: 6px; }
  .pp-form-block { display: flex; flex-direction: column; gap: 6px; }

  /* ---- the ink switch. A hollow ring at OFF that closes into a solid disc at ON;
   * the well fills with ink. State is fill and shape, not a hue. Port of
   * App/Host/DesignSystem/InkSwitch.swift. */
  .pp-switch {
    appearance: none; -webkit-appearance: none; position: relative; flex: none; margin: 0;
    width: 56px; height: 32px; border-radius: 999px; background: var(--well); cursor: pointer;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .16);
    transition: background var(--m-base) var(--ease-house), box-shadow var(--m-base) var(--ease-house);
  }
  .pp-switch::before {
    content: ""; position: absolute; top: 4px; left: 4px; width: 24px; height: 24px; border-radius: 50%;
    background: radial-gradient(circle, transparent 7px, var(--text-dim) 7.5px);
    transition: transform var(--spring-switch-dur) var(--spring-switch), background var(--m-base) var(--ease-house);
  }
  .pp-switch:checked { background: var(--text); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .28); }
  .pp-switch:checked::before { transform: translateX(24px); background: var(--panel); }
  .pp-switch:disabled { opacity: .4; cursor: default; }
  .pp-switch-row { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); padding: var(--s-3) 0; min-height: 58px; }
  .pp-switch-row + .pp-switch-row { border-top: 1px solid var(--edge); }
  .pp-switch-row .pp-hint { margin-top: 2px; }

  /* ---- choosing one of few. A bar under the chosen word, one bar that travels. The
   * tray and its thumb are gone (2026-09-07: "trays are bad"); the app's rungs said it
   * first, a bar UNDER the word, not a filled pill around it. Set --n and --i as before. */
  .pp-seg { position: relative; display: grid; grid-template-columns: repeat(var(--n, 2), 1fr); padding: 0; --i: 0; }
  .pp-seg-thumb {
    position: absolute; bottom: 4px; left: 0; width: calc(100% / var(--n, 2)); height: 2px; border-radius: 1px; background: var(--catch);
    transform: translateX(calc(100% * var(--i))) scaleX(.6); transform-origin: center; transition: transform var(--spring-switch-dur) var(--spring-switch); pointer-events: none;
  }
  .pp-seg > button {
    position: relative; z-index: 1; min-height: 40px; padding: 0 10px 2px; border: 0; background: none; cursor: pointer;
    font-family: var(--program); font-weight: 500; font-size: 14px; color: var(--text-dim);
    transition: color var(--m-base) var(--ease-house);
  }
  .pp-seg > button[aria-pressed="true"], .pp-seg > button[aria-selected="true"] { font-weight: 700; }
  .pp-seg > button[aria-pressed="true"], .pp-seg > button[aria-selected="true"] { color: var(--text); }

  /* ---- rungs. A setting with three or four DISCRETE states differs in kind, not
   * degree; a slider would say continuum. Words, with a bar under the chosen one. */
  .pp-rungs { display: flex; gap: var(--s-2); }
  .pp-rungs > button {
    position: relative; flex: 1; min-height: var(--ctl-sm); padding: 8px 4px; border: 0; background: none; cursor: pointer;
    font-family: var(--program); font-size: var(--p-hint); font-weight: 500; color: var(--text-dim); text-align: center;
    transition: color var(--m-base) var(--ease-house);
  }
  .pp-rungs > button::after {
    content: ""; position: absolute; left: 20%; right: 20%; bottom: 2px; height: 2px; background: var(--catch);
    transform: scaleX(0); transition: transform var(--m-base) var(--ease-house);
  }
  .pp-rungs > button[aria-pressed="true"] { color: var(--text); font-weight: 700; }
  .pp-rungs > button[aria-pressed="true"]::after { transform: scaleX(1); }
  .pp-rungs > button b { display: block; font-size: 12px; font-weight: 600; color: var(--text-faint); margin-top: 2px; }
  .pp-rungs > button[aria-pressed="true"] b { color: var(--text-dim); }

  /* ---- the setting tile. A setting shaped like a control: no box, a glyph that is
   * bright when on and dim when off, a title, and the line that says what a guest
   * can now do. Port of App/Host/DesignSystem/SettingTile.swift. */
  .pp-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--s-2) var(--s-5); }
  .pp-tile {
    display: flex; flex-direction: column; align-items: flex-start; gap: 6px; text-align: left;
    padding: var(--s-3) 0; border: 0; background: none; cursor: pointer; color: var(--text-faint);
    min-height: 96px; text-decoration: none;
  }
  .pp-tile svg { width: 21px; height: 21px; stroke: currentColor; fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; color: var(--text-dim); transition: color var(--m-base) var(--ease-house), transform var(--spring-seat-dur) var(--spring-seat); }
  .pp-tile[aria-pressed="true"] svg, .pp-tile.is-on svg { color: var(--catch); }
  .pp-tile-title { font-family: var(--program); font-weight: 600; font-size: 14px; color: var(--text-faint); transition: color var(--m-base) var(--ease-house); }
  .pp-tile[aria-pressed="true"] .pp-tile-title, .pp-tile.is-on .pp-tile-title, .pp-tile[data-static] .pp-tile-title { color: var(--text); }
  .pp-tile-line { font-size: 11px; line-height: 1.35; color: var(--text-dim); min-height: 2.7em; }
  .pp-tile[data-tone] .pp-tile-line { color: var(--hue-yellow); }
  .pp-tile:disabled { opacity: .45; cursor: default; }
  .pp-tile.is-late svg { animation: pp-late 1.1s var(--ease-house) infinite alternate; }
  @keyframes pp-late { to { opacity: .55; } }

  /* ---- readouts. Label over figure, chromeless, on the room. The figure ROLLS:
   * shell.js PPHost.roll(el, value) swaps it through .pp-roll-out / .pp-roll-in. */
  .pp-readout { display: flex; flex-direction: column; gap: 3px; }
  .pp-readout-label { font-family: var(--marquee); font-weight: 500; font-size: var(--t-chip); letter-spacing: .18em; text-transform: uppercase; color: var(--text-faint); }
  .pp-readout-figure {
    font-family: var(--marquee); font-weight: 500; font-size: 34px; line-height: 1; color: var(--text);
    font-variant-numeric: tabular-nums; display: inline-flex; align-items: baseline; gap: 6px; overflow: hidden;
  }
  .pp-readout-figure[data-size="sm"] { font-size: 22px; }
  .pp-readout-figure[data-size="lg"] { font-size: 48px; }
  .pp-readout-figure small { font-family: var(--program); font-weight: 400; font-size: 15px; color: var(--text-faint); }
  .pp-readout-delta { font-size: var(--p-hint); color: var(--text-dim); }
  [data-tone="hearts"] { color: var(--hue-red); }
  [data-tone="photos"] { color: var(--hue-orange); }
  [data-tone="capacity"] { color: var(--hue-yellow); }
  [data-tone="guests"], [data-tone="time"] { color: var(--hue-green); }
  [data-tone="dates"] { color: var(--hue-purple); }
  .pp-roll { display: inline-block; }
  .pp-roll-in { animation: pp-roll-in 280ms var(--ease-house) both; }
  .pp-roll-out { animation: pp-roll-out 280ms var(--ease-house) both; position: absolute; }
  @keyframes pp-roll-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
  @keyframes pp-roll-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(-14px); } }
  @media (prefers-reduced-motion: reduce) { .pp-roll-in, .pp-roll-out { animation: none; } .pp-roll-out { display: none; } }

  /* ---- the KPI. The poster's band: a 17px figure over a 9px tracked label. It sits
   * on a photograph, so every LEAF carries the rail shadow; never the wrapper. */
  .pp-kpis { display: flex; gap: var(--s-5); flex-wrap: wrap; }
  .pp-kpi { display: flex; flex-direction: column; gap: 2px; color: var(--text); }
  .pp-kpi-figure { font-family: var(--marquee); font-weight: 500; font-size: var(--t-kpi); line-height: 1; font-variant-numeric: tabular-nums; filter: var(--sh-rail); }
  .pp-kpi-label { font-family: var(--marquee); font-weight: 500; font-size: var(--t-kpi-label); letter-spacing: var(--trk-kpi-label); text-transform: uppercase; color: rgba(255, 255, 255, .78); filter: var(--sh-rail); }
  .pp-kpi[data-tone] .pp-kpi-figure { color: inherit; }

  /* ---- meters and dials wrap charts.js output; the hue rides on `color:`. */
  .pp-meter { height: 5px; border-radius: 999px; background: var(--well); overflow: hidden; }
  .pp-meter > i { display: block; height: 100%; border-radius: 999px; background: currentColor; transform-origin: left; transition: transform 600ms var(--ease-house); }
  .pp-dial { width: 112px; height: 112px; }

  /* ---- the pip. A dot and a word. Live breathes (opacity only; nothing under
   * reduced motion). Draft, paused and ended are faint. */
  .pp-pip { display: inline-flex; align-items: center; gap: 6px; font-family: var(--marquee); font-weight: 500; font-size: var(--t-eyebrow); letter-spacing: .18em; text-transform: uppercase; color: var(--text-dim); }
  .pp-pip::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--text-faint); flex: none; }
  .pp-pip[data-state="live"] { color: var(--text); }
  .pp-pip[data-state="live"]::before { background: var(--catch); box-shadow: 0 0 0 4px rgba(244, 244, 245, .12); animation: pp-breathe 1.2s var(--ease-house) infinite alternate; }
  .pp-pip[data-state="suspended"] { color: var(--text-dim); }
  @keyframes pp-breathe { to { opacity: .55; } }
  @media (prefers-reduced-motion: reduce) { .pp-pip[data-state="live"]::before { animation: none; } }

  /* ---- the chip. Ten-point tracked caps, no strokes. Bare by default. */
  .pp-chip { display: inline-flex; align-items: center; gap: 5px; font-family: var(--marquee); font-weight: 500; font-size: var(--t-chip); letter-spacing: var(--trk-chip); text-transform: uppercase; color: var(--text-dim); line-height: 1; }
  .pp-chip[data-tone="catch"] { color: var(--catch); background: var(--catch-wash); border-radius: var(--r-xs); padding: 4px 7px; }
  .pp-chip[data-tone="ticket"] { color: var(--text); background: rgba(0, 0, 0, .4); border-radius: var(--r-xs); padding: 4px 7px; }

  /* ---- the ledger. Billing lines: a kicker heading, tabular figures, hairlines. */
  .pp-ledger { width: 100%; border-collapse: collapse; font-size: 14px; }
  .pp-ledger th { text-align: left; padding: 8px 10px 8px 0; font-family: var(--marquee); font-weight: 500; font-size: var(--t-eyebrow); letter-spacing: var(--trk-eyebrow); text-transform: uppercase; color: var(--text-faint); border-bottom: 1px solid var(--edge); }
  .pp-ledger td { padding: 11px 10px 11px 0; border-bottom: 1px solid var(--edge); font-variant-numeric: tabular-nums; color: var(--text); }
  .pp-ledger td.pp-dim { color: var(--text-dim); }
  .pp-ledger .r { text-align: right; padding-right: 0; }
  .pp-ledger tr:last-child td { border-bottom: 0; }
  .pp-ledger-total td { font-family: var(--marquee); font-size: var(--t-kpi); }

  /* ---- surfaces ---------------------------------------------------------- */
  /* The panel: the only card. Flat panel ink, one hairline, radius 20. No gradient,
   * no ring-top, no shadow: a lit tile is the chromed tell. */
  .pp-panel { background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r-card); padding: 18px; }
  .pp-panel + .pp-panel { margin-top: var(--s-4); }
  .pp-panel > .pp-kicker { margin-bottom: var(--s-3); }
  .pp-stack { display: flex; flex-direction: column; gap: var(--s-4); }
  .pp-grid { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }

  /* The poster: the pool's own photograph (or its printed empty cover), a scrim that
   * thins toward the head, the name, and either the KPI band or the doors hint. The
   * hint is WORDS AND A CHEVRON, never a fake key: a picture of a button that does
   * nothing when you press it is the one thing a card must never carry. */
  .pp-poster {
    position: relative; display: block; aspect-ratio: 4 / 3.1; border-radius: var(--r-poster); overflow: hidden;
    background: var(--raised); color: var(--text); text-decoration: none; box-shadow: var(--sh-poster);
    transition: transform var(--m-base) var(--ease-house);
  }
  .pp-poster::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .14); pointer-events: none; }
  .pp-poster:hover { transform: translateY(-3px); }
  .pp-poster:active { transform: scale(.98); }
  .pp-poster-cover, .pp-poster-cover > img, .pp-poster-cover > svg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
  .pp-poster-scrim { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(7, 9, 13, .10) 30%, rgba(7, 9, 13, .86) 100%); }
  .pp-poster-body { position: absolute; left: 20px; right: 20px; bottom: 18px; display: flex; flex-direction: column; gap: 10px; }
  .pp-poster-name { font-family: var(--marquee); font-weight: 500; font-size: 26px; line-height: 1.05; letter-spacing: .02em; text-transform: uppercase; filter: var(--sh-rail); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .pp-poster-meta { font-size: 13.5px; color: rgba(255, 255, 255, .8); font-variant-numeric: tabular-nums; filter: var(--sh-rail); }
  .pp-poster-doors { display: inline-flex; align-items: center; gap: 4px; font-family: var(--marquee); font-weight: 500; font-size: var(--t-cta); letter-spacing: var(--trk-cta); filter: var(--sh-rail); }
  .pp-poster-doors .pp-chevron { width: 16px; height: 16px; color: currentColor; transition: transform var(--m-base) var(--ease-house); }
  .pp-poster:hover .pp-poster-doors .pp-chevron { transform: translateX(3px); }
  .pp-poster .pp-pip { position: absolute; top: 14px; left: 16px; filter: var(--sh-rail); color: rgba(255, 255, 255, .9); }
  .pp-poster .pp-pip::before { filter: none; }
  .pp-empty-cover { width: 100%; height: 100%; }

  /* The list row: no tile, no outline. An image at the head, a chevron at the tail,
   * a hairline between neighbours. Selection is a 3px bar down the leading edge. */
  .pp-rows { display: flex; flex-direction: column; }
  .pp-row {
    display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: var(--s-3);
    min-height: 58px; padding: 11px 0; border-top: 1px solid var(--edge); color: var(--text); text-decoration: none;
    background: none; border-left: 0; border-right: 0; border-bottom: 0; width: 100%; text-align: left; cursor: pointer;
  }
  .pp-row:first-child { border-top: 0; }
  .pp-row--static { cursor: default; }
  .pp-row-head { width: 44px; height: 44px; border-radius: var(--r-sm); background: var(--raised); overflow: hidden; flex: none; display: flex; align-items: center; justify-content: center; color: var(--text-dim); }
  .pp-row-head > img, .pp-row-head > svg.pp-empty-cover { width: 100%; height: 100%; object-fit: cover; }
  .pp-row-head > svg:not(.pp-empty-cover) { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
  .pp-row-head--lg { width: 64px; height: 64px; }
  .pp-row-body { min-width: 0; }
  .pp-row-title { display: block; font-weight: 600; font-size: var(--p-body); color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pp-row-sub { display: block; font-size: var(--p-hint); color: var(--text-dim); margin-top: 2px; font-variant-numeric: tabular-nums; }
  .pp-row-tail { display: flex; align-items: center; gap: var(--s-2); color: var(--text-dim); font-size: var(--p-hint); font-variant-numeric: tabular-nums; }
  .pp-chevron { display: inline-block; width: 22px; height: 22px; color: var(--text-faint); stroke: currentColor; fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; flex: none; transition: transform var(--spring-seat-dur) var(--spring-seat); }
  .pp-chevron > svg { width: 100%; height: 100%; display: block; }
  [aria-expanded="true"] > .pp-row-tail .pp-chevron, [aria-expanded="true"] > .pp-chevron { transform: rotate(90deg); }
  .pp-row[aria-current="true"], .pp-row.is-selected { box-shadow: inset 3px 0 0 var(--catch); padding-left: var(--s-3); }
  .pp-row.is-off .pp-row-title { color: var(--text-dim); }
  .pp-rows-head { padding: var(--s-4) 0 var(--s-2); }

  /* The sheet. A panel that rises from the foot on a phone and grows in place on a
   * laptop. Never cross-fades between two layouts. */
  .pp-sheet-scrim { position: fixed; inset: 0; z-index: var(--z-sheet); display: flex; align-items: flex-end; justify-content: center; background: rgba(0, 0, 0, .5); animation: pp-fade var(--m-base) var(--ease-house) both; }
  .pp-sheet-scrim[hidden] { display: none; }
  .pp-sheet {
    position: relative; width: 100%; max-height: 82vh; max-height: 82dvh; overflow-y: auto; overscroll-behavior: contain;
    background: var(--panel); border: 1px solid var(--line2); border-radius: var(--r-sheet) var(--r-sheet) 0 0;
    padding: 10px 20px calc(24px + var(--safe-b)); box-shadow: var(--sh-sheet);
    animation: pp-sheet-up var(--spring-deck-dur) var(--spring-deck) both;
  }
  .pp-sheet-handle { width: 40px; height: 5px; border-radius: 3px; background: var(--raised); margin: 6px auto 14px; }
  .pp-sheet-title { font-family: var(--marquee); font-weight: 500; font-size: var(--t-sheet); margin: 4px 0 var(--s-3); color: var(--text); }
  .pp-sheet-scrim.is-leaving { animation: pp-fade 440ms var(--ease-house) reverse both; }
  .pp-sheet-scrim.is-leaving .pp-sheet { animation: pp-sheet-up 440ms var(--ease-house) reverse both; }
  @keyframes pp-sheet-up { from { transform: translateY(100%); } to { transform: none; } }
  @keyframes pp-fade { from { opacity: 0; } to { opacity: 1; } }
  @media (min-width: 720px) {
    .pp-sheet-scrim { align-items: center; padding: 24px; }
    .pp-sheet { max-width: 520px; border-radius: var(--r-sheet); animation: pp-sheet-grow var(--m-cine) var(--ease-cine) both; }
    .pp-sheet-scrim.is-leaving .pp-sheet { animation: pp-sheet-grow 300ms var(--ease-house) reverse both; }
    .pp-sheet-handle { display: none; }
  }
  @keyframes pp-sheet-grow { from { opacity: 0; transform: scale(.98); } to { opacity: 1; transform: none; } }

  /* The notice. WORDS at the foot, not a card (the card was rejected on sight): bare
   * leaves with the rail shadow over an edgeless darkening, the one ground the job test
   * allows for legibility. ui.js's toast lands on the same element (#toast). */
  .pp-notice, .ts-toast {
    position: fixed; left: 0; right: 0; width: fit-content; max-width: min(88vw, 520px); margin: 0 auto;
    bottom: calc(30px + var(--safe-b)); z-index: var(--z-notice); isolation: isolate;
    padding: 8px 12px; color: var(--text); filter: var(--sh-rail);
    font-size: var(--p-hint); font-weight: 500; line-height: 1.4;
    opacity: 0; pointer-events: none; transform: translateY(16px);
    transition: opacity var(--m-base) var(--ease-house), transform var(--spring-seat-dur) var(--spring-seat);
  }
  .pp-notice::before, .ts-toast::before { content: ""; position: absolute; inset: -18px -30px; z-index: -1; border-radius: 60px; background: var(--void); opacity: .9; filter: blur(24px); pointer-events: none; }
  .pp-notice.show, .ts-toast.show { opacity: 1; transform: none; pointer-events: auto; }
  .pp-notice--err, .ts-toast--err { color: var(--error); }
  .pp-notice--ok, .ts-toast--ok { color: var(--text); }
  .pp-notice button { background: none; border: 0; padding: 0 0 0 12px; color: var(--text); font-weight: 700; cursor: pointer; }
  @media (max-width: 639px) { .pp-notice, .ts-toast { bottom: calc(49px + 16px + var(--safe-b)); } }

  /* The skeleton: raised ink with a sweep. Static under reduced motion. */
  .pp-skel { position: relative; overflow: hidden; border-radius: var(--r-xs); background: var(--raised); color: transparent; }
  .pp-skel::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .06), transparent); transform: translateX(-100%); animation: pp-sweep 1.4s var(--ease-house) infinite; }
  @keyframes pp-sweep { to { transform: translateX(100%); } }
  @media (prefers-reduced-motion: reduce) { .pp-skel::after { animation: none; } }

  /* The monogram: a letter in a raised disc. The account chip. */
  .pp-monogram { width: 32px; height: 32px; border-radius: 50%; background: var(--raised); box-shadow: inset 0 0 0 1px var(--line2); display: inline-flex; align-items: center; justify-content: center; font-family: var(--marquee); font-weight: 500; font-size: 13px; color: var(--text); flex: none; }

  /* ---- the layers that survived the trays-and-popups round (2026-09-07), promoted from
   * the board so the shop can wear them. NOTHING FLOATS IN A BOX. */
  /* THE EDGELESS GROUND. The one ground the job test allows for legibility: no drawn shape. */
  .edgeless { position: relative; isolation: isolate; }
  .edgeless::before { content: ""; position: absolute; inset: -18px -28px; z-index: -1; border-radius: 60px; background: var(--void); opacity: .9; filter: blur(24px); pointer-events: none; }
  /* ONE WAY TO CHOOSE ONE OF FEW: a bar under the chosen word, one bar that travels
   * (shell.js PPHost.rungs seats it from the chosen label's own box). */
  .rungs { position: relative; display: inline-flex; flex-wrap: wrap; gap: 2px; --x: 0px; --w: 0; }
  .rungs button { position: relative; padding: 9px 10px 11px; border: 0; background: none; cursor: pointer; font-family: var(--program); font-weight: 500; font-size: 14px; color: var(--text-dim); transition: color var(--m-base) var(--ease-house); }
  .rungs button[aria-pressed="true"] { color: var(--text); font-weight: 700; }
  .rungs button:hover { color: var(--text); }
  .rungs button:disabled { color: var(--text-faint); cursor: default; }
  .rungs button b { display: block; font-weight: 500; font-size: 11px; color: var(--text-faint); margin-top: 2px; font-variant-numeric: tabular-nums; }
  .rungs button[aria-pressed="true"] b { color: var(--text-dim); }
  .rungs .bar { position: absolute; left: 0; bottom: 4px; width: 1px; height: 2px; border-radius: 1px; background: var(--catch); transform-origin: left center; transform: translateX(var(--x)) scaleX(var(--w)); transition: transform var(--spring-switch-dur) var(--spring-switch); }
  /* THE POPOVER: bare rows over an edgeless darkening, anchored under what opened it. */
  .pop { position: absolute; top: calc(100% + 10px); left: -6px; z-index: 12; width: 320px; max-width: calc(100vw - 32px); padding: 8px 6px 4px; transform-origin: top left; animation: pop-in var(--spring-seat-dur) var(--spring-seat) both; }
  .pop::before { inset: -22px -30px -24px; opacity: .93; filter: blur(26px); }
  .pop.is-leaving { animation: pop-in 160ms var(--ease-house) reverse both; }
  .pop[hidden] { display: none; }
  @keyframes pop-in { from { opacity: 0; transform: translateY(-6px) scale(.96); } to { opacity: 1; transform: none; } }
  /* THE REPORT: a failed act says so under itself, and the line stays until the next act. */
  .report { display: block; margin-top: 2px; font-size: 12px; line-height: 1.4; color: var(--text-dim); padding-left: 10px; box-shadow: inset 3px 0 0 var(--error); border-radius: 2px; max-width: 34ch; }
  .report:empty { display: none; }
  @keyframes strip-in { from { opacity: 0; transform: translateX(8px); } to { opacity: 1; transform: none; } }
  .is-strip-in { animation: strip-in var(--spring-seat-dur) var(--spring-seat) both; }

  /* Animated glyphs (js/host/glyphs.js): a box the SVG fills; colour inherits. */
  .pp-glyph { display: inline-block; width: 1em; height: 1em; flex: none; vertical-align: -.14em; }
  .pp-glyph > svg { width: 100%; height: 100%; display: block; }

  /* Inline glyphs from icons.js: size to the text, inherit colour. */
  .ic { width: 1em; height: 1em; display: inline-block; vertical-align: -.14em; flex: none; }
  svg.ic { stroke: currentColor; fill: none; }
  [data-icon] { display: inline-flex; }

  /* Entrances. One gesture for the whole console: opacity, 14px of lift, a little
   * blur, on the cine curve, staggered down the page. shell.js applies .pp-enter
   * ONCE on load to [data-enter] and its children carry --i; nothing re-animates
   * on scroll. Blur is filter, which is not compositor-cheap, so it is small and
   * short and it is removed with will-change when the animation ends. */
  .pp-enter { animation: pp-enter var(--m-cine) var(--ease-cine) both; animation-delay: calc(var(--i, 0) * var(--stagger)); will-change: transform, opacity, filter; }
  @keyframes pp-enter { from { opacity: 0; transform: translateY(var(--move-md)); filter: blur(6px); } to { opacity: 1; transform: none; filter: blur(0); } }
  @media (prefers-reduced-motion: reduce) { .pp-enter { animation: none; } }
}

/* ============================================================ shell ======= */
@layer shell {
  .skip-link { position: absolute; left: 8px; top: -56px; z-index: var(--z-skip); background: var(--key); color: var(--on-key); text-decoration: none; padding: 10px 14px; border-radius: var(--r-sm); font-weight: 600; font-size: 14px; transition: top var(--m-base); }
  .skip-link:focus { top: 8px; }

  .pp-room { position: fixed; inset: 0; z-index: var(--z-room); pointer-events: none; }
  .pp-room[hidden] { display: none; } /* the reset's canvas { display: block } would win over [hidden] */

  /* The page sits over the room. 54px for the HUD; under 980px the rail takes the foot. */
  .pp-page { position: relative; z-index: var(--z-page); max-width: 1160px; margin: 0 auto; padding: 54px clamp(16px, 3vw, 32px) 48px; }
  @media (max-width: 639px) { .pp-page { padding-bottom: calc(49px + 28px + var(--safe-b)); } }
  html[data-chrome="bare"] .pp-page, html[data-chrome="none"] .pp-page, body:has(.pp-hud[data-mode="page"]) .pp-page { padding-left: clamp(16px, 3vw, 32px); }

  /* THE HUD. 54px, transparent, over the room. It recedes as content rises: shell.js
   * writes --recede (0..1, ten steps) from the page's own hand-off, and the bar lifts
   * 46px and is gone by .56 of the travel. Pointer events pass through the empty bar. */
  .pp-hud {
    position: fixed; top: 0; left: 0; right: 0; height: 54px; z-index: var(--z-hud);
    display: flex; align-items: center; gap: 10px; padding: 0 16px; --recede: 0;
    transform: translateY(calc(-46px * var(--recede))); opacity: clamp(0, calc(1 - var(--recede) / .56), 1);
    pointer-events: none; transition: transform var(--m-base) var(--ease-house), opacity var(--m-base) var(--ease-house);
  }
  .pp-hud > * { pointer-events: auto; }
  .pp-hud[hidden] { display: none; }
  .pp-hud-btn {
    display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 50%;
    border: 0; background: none; cursor: pointer; color: var(--text); text-decoration: none; filter: var(--sh-rail);
  }
  .pp-hud-btn svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
  .pp-hud-btn .pp-glyph { width: 22px; height: 22px; }
  .pp-hud-btn[hidden] { display: none; }
  .pp-hud-danger { color: var(--error); font-family: var(--marquee); font-weight: 500; font-size: var(--t-cta); letter-spacing: var(--trk-cta); width: auto; padding: 0 12px; border-radius: var(--r-ctl); white-space: nowrap; }
  .pp-hud-title { font-family: var(--program); font-weight: 600; font-size: var(--p-header); color: var(--text); filter: var(--sh-rail); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  .pp-hud-title[hidden] { display: none; }
  .pp-hud-actions { margin-left: auto; display: flex; align-items: center; gap: 2px; }
  .pp-hud[data-mode="page"] .pp-hud-mark, .pp-hud[data-mode="page"] .pp-rail { display: none; }

  /* The wordmark: three copies of the seam partial, stacked. The two passes sit under
   * the face at --misreg 0; PPHost.kick throws them out and they spring back. */
  .pp-hud-mark { position: relative; display: block; height: 19px; width: calc(19px * 264 / 36); flex: none; --misreg: 0; --misreg-dur: 180ms; --misreg-ease: var(--ease-house); }
  .pp-hud-mark .pp-pass, .pp-hud-mark .pp-face { position: absolute; inset: 0; display: block; }
  .pp-hud-mark .mark { display: block; width: 100%; height: 100%; }
  .pp-hud-mark .mark rect { fill: currentColor; }
  .pp-hud-mark .pp-face { color: var(--text); filter: var(--sh-rail); }
  .pp-hud-mark .pp-pass { transition: transform var(--misreg-dur) var(--misreg-ease); }
  .pp-hud-mark .pp-pass-a { color: var(--pass-a); transform: translate(calc(-1px * var(--misreg)), calc(-.7px * var(--misreg))); }
  .pp-hud-mark .pp-pass-b { color: var(--pass-b); transform: translate(calc(1px * var(--misreg)), calc(.7px * var(--misreg))); }

  /* THE FOLD. The seam's own collapse (public/design/seam.css §13), keyed on .is-folded:
   * the first P's stem is the hinge and never moves, the last o rolls the length of the
   * word and lands as the mark's diamond, everything else clears right-to-left. The box
   * does NOT narrow; the HUD's actions are pinned right by margin-left:auto. shell.js
   * writes --bar-d / --bar-lx / --bar-ly per log once at boot, from the log's real x.
   * Transitions, not keyframes: a scroll state has to be reversible mid-flight. */
  .pp-hud-mark .mark rect, .pp-hud-mark .mark .roller { transition: transform 620ms var(--ease-house) var(--bar-d, 0ms), opacity 300ms linear var(--bar-d, 0ms); }
  .pp-hud-mark .mark .gl, .pp-hud-mark .mark rect { transform-box: fill-box; transform-origin: center; }
  .pp-hud-mark .mark rect { transform-origin: 0 0; }
  .pp-hud-mark .mark .roller { transform-box: fill-box; transform-origin: center; }
  .pp-hud-mark.is-folded .mark rect:not(.keeps):not(.o-log) { opacity: 0; transform: translate(var(--bar-lx, 0), var(--bar-ly, 0)); }
  .pp-hud-mark.is-folded .mark .o-log { transform: translate(var(--dx), var(--dy)) scale(var(--kx), var(--ky)); }
  .pp-hud-mark.is-folded .mark .roller { transform: rotate(-135deg); }
  /* Folded, the P heads the rail's column: the mark box slides 14px so the P's centre
   * (27 units wide at 19px tall, from the bar's 16px padding) sits on the column's axis at
   * 38px. The vertical part is the bar's recede, kept so the two never fight. */
  .pp-hud .pp-hud-mark.is-folded { transform: translate(14px, calc(-46px * var(--recede))); }
  @media (prefers-reduced-motion: reduce) { .pp-hud-mark .mark rect, .pp-hud-mark .mark .roller, .pp-hud-mark .pp-pass { transition-duration: 1ms; transition-delay: 0ms; } }

  /* THE RAIL. The app's tab bar, and it FOLDS WITH THE WORDMARK: inline in the bar after
   * the mark while the page sits at the top; once the page has scrolled and the word has
   * folded to the P, the four glyphs recess up into the bar and slide in from the side as
   * a column under it on the left edge, where they cover nothing. Under 640px there is no
   * width to spare, so a phone keeps a bottom bar at every scroll, where a thumb is. The bar itself never transforms (a transformed
   * ancestor would become the fixed rail's containing block); the recede moves its parts. */
  .pp-hud { transform: none; opacity: 1; }
  .pp-hud > :not(.pp-rail) { transform: translateY(calc(-46px * var(--recede))); opacity: clamp(0, calc(1 - var(--recede) / .56), 1); transition: transform var(--m-base) var(--ease-house), opacity var(--m-base) var(--ease-house); }
  .pp-rail { display: flex; align-items: center; gap: 2px; margin-left: 18px; }
  .pp-rail-item {
    position: relative; display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    min-width: 56px; height: 48px; padding: 0 8px; color: var(--text-dim); text-decoration: none; border-radius: var(--r-sm);
    font-family: var(--program); font-weight: 600; font-size: 11px; letter-spacing: .02em; line-height: 1;
    transition: color var(--m-base) var(--ease-house);
  }
  .pp-rail-item svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round; }
  .pp-rail-item .pp-glyph { width: 20px; height: 20px; }
  .pp-rail-item::after { content: ""; position: absolute; left: 16px; right: 16px; bottom: 3px; height: 2px; border-radius: 1px; background: var(--catch); transform: scaleX(0); transition: transform var(--m-base) var(--ease-house); }
  .pp-rail-item:hover { color: var(--text); }
  .pp-rail-item.is-active { color: var(--text); }
  .pp-rail-item.is-active::after { transform: scaleX(1); }
  .pp-rail-item[hidden] { display: none; }
  /* The bag's count: a bare marquee figure at the glyph's shoulder with the rail shadow, no
   * disc. cart.js rolls it (PPHost.roll); .is-hit is the seat spring when something lands. */
  .pp-rail-count { position: absolute; top: 3px; left: calc(50% + 7px); font-family: var(--marquee); font-weight: 500; font-size: 11px; line-height: 1; color: var(--text); font-variant-numeric: tabular-nums; filter: var(--sh-rail); pointer-events: none; }
  .pp-rail-count[hidden] { display: none; }
  .pp-rail-item.is-hit .pp-glyph { animation: pp-hit var(--spring-seat-dur) var(--spring-seat) both; }
  @keyframes pp-hit { 0% { transform: scale(1); } 35% { transform: scale(1.18); } 100% { transform: scale(1); } }
  @media (prefers-reduced-motion: reduce) { .pp-rail-item.is-hit .pp-glyph { animation: none; } }
  /* The two beats. Recess: up into the top of the bar. Slide: in from the side. */
  @keyframes pp-rail-recess { to { opacity: 0; transform: translateY(-12px); } }
  @keyframes pp-rail-slide-in { from { opacity: 0; transform: translateX(-18px); } to { opacity: 1; transform: none; } }
  @keyframes pp-rail-slide-out { to { opacity: 0; transform: translateX(-18px); } }
  @keyframes pp-rail-drop { from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: none; } }
  @media (min-width: 640px) {
    /* Room for the folded column, held constant so the fold never reflows the page. */
    .pp-page { padding-left: 84px; }
    .pp-hud.is-folding .pp-rail-item { animation: pp-rail-recess 170ms var(--ease-house) forwards; animation-delay: calc(var(--i, 0) * 25ms); }
    .pp-hud.is-folded .pp-rail {
      position: fixed; left: 10px; top: 60px; margin: 0; z-index: var(--z-rail); pointer-events: auto;
      flex-direction: column; align-items: stretch; gap: 2px; width: 56px;
    }
    .pp-hud.is-folded .pp-rail-item { min-width: 0; width: 56px; height: 54px; padding: 0; animation: pp-rail-slide-in var(--m-slow) var(--ease-cine) both; animation-delay: calc(var(--i, 0) * 45ms); }
    .pp-hud.is-folded .pp-rail-item .pp-glyph { width: 22px; height: 22px; }
    .pp-hud.is-folded .pp-rail-item::after { left: 0; right: auto; top: 13px; bottom: 13px; width: 2px; height: auto; transform: scaleY(0); transform-origin: top; }
    .pp-hud.is-folded .pp-rail-item.is-active::after { transform: scaleY(1); }
    .pp-hud.is-unfolding .pp-rail-item { animation: pp-rail-slide-out 170ms var(--ease-house) forwards; animation-delay: calc(var(--i, 0) * 25ms); }
    .pp-hud.is-unfolded .pp-rail-item { animation: pp-rail-drop var(--m-slow) var(--ease-cine) both; animation-delay: calc(var(--i, 0) * 45ms); }
  }
  @media (max-width: 639px) {
    .pp-rail {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-rail); margin: 0;
      height: calc(49px + var(--safe-b)); padding: 0 8px var(--safe-b); justify-content: space-around;
      background: var(--glass); backdrop-filter: blur(18px) saturate(1.2); -webkit-backdrop-filter: blur(18px) saturate(1.2);
      pointer-events: auto;
    }
    .pp-rail-item { flex: 1; height: 49px; }
    .pp-rail-item::after { bottom: 4px; }
  }
  .pp-hud[data-mode="page"] .pp-rail { display: none; }
  @media (prefers-reduced-motion: reduce) { .pp-rail-item { animation: none !important; } }
  /* Chromeless surfaces: the print and invoice documents, the two editors, the photobook. */
  html[data-chrome="none"] .pp-hud, html[data-chrome="none"] .pp-rail { display: none; }
  html[data-chrome="none"] .pp-page { padding-top: 0; padding-bottom: 0; max-width: none; }
  html[data-chrome="bare"] .pp-rail { display: none; }
  html[data-chrome="bare"] .pp-page { padding-bottom: 48px; }

}

/* =========================================================== compat ======= */
/* The OLD console's names, on the new room, so every page renders today. Each block
 * lists its consumers; delete the block when the last one is ported. Nothing here is
 * the target look, only a stand-in that obeys the room's rules (flat, one hairline,
 * no glow). */
@layer compat {
  /* consumers: every unported page. */
  .app-content { max-width: 1160px; margin: 0 auto; padding: var(--s-4) 0 var(--s-6); }
  .app-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-4); flex-wrap: wrap; margin: var(--s-4) 0 var(--s-5); }
  .app-head .shell-head { margin: 0; }
  .shell-head { margin: 0 0 var(--s-4); }
  .shell-head h1 { font-family: var(--marquee); font-weight: 500; font-size: var(--t-room); letter-spacing: var(--trk-room); text-transform: uppercase; color: var(--text); line-height: 1.1; }
  .shell-head p { margin-top: 4px; color: var(--text-dim); font-size: var(--p-hint); }
  .app-back { display: inline-flex; align-items: center; gap: 6px; color: var(--text-dim); text-decoration: none; font-size: var(--p-hint); font-weight: 600; margin-bottom: var(--s-3); }
  .app-back:hover { color: var(--text); }
  .app-sidebar, .app-mobilebar, .app-scrim, .tsn-nav { display: none !important; }
  .card { background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r-card); padding: 18px; margin-bottom: var(--s-4); }
  .card h2 { font-family: var(--marquee); font-weight: 500; font-size: var(--t-card); margin: 0 0 var(--s-3); color: var(--text); }
  .card h3 { font-family: var(--marquee); font-weight: 500; font-size: var(--t-kicker); letter-spacing: var(--trk-kicker); text-transform: uppercase; color: var(--text-dim); margin: 0 0 var(--s-2); }
  .note { font-size: var(--p-foot); color: var(--text-dim); line-height: 1.5; }
  .wrap { max-width: 860px; margin: 0 auto; padding: var(--s-4) 0; }
  .wrap-narrow { max-width: 560px; }
  .row { display: flex; gap: var(--s-3); align-items: flex-end; }
  .row > div { flex: 1; min-width: 0; }
  .section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-3); margin: var(--s-5) 0 var(--s-3); }
  .section-head h2 { font-family: var(--marquee); font-weight: 500; font-size: var(--t-section); letter-spacing: var(--trk-section); text-transform: uppercase; color: var(--text); }
  .section-head a { font-size: var(--p-hint); font-weight: 600; color: var(--text-dim); }

  /* consumers: bare controls on every unported page (event, account, qr, shop, editor…). */
  label:not([class*="pp-"]) { display: block; font-family: var(--marquee); font-weight: 500; font-size: var(--t-kicker); letter-spacing: var(--trk-kicker); text-transform: uppercase; color: var(--text-dim); margin: var(--s-3) 0 6px; }
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]), select, textarea {
    width: 100%; min-height: var(--ctl-sm); padding: 10px 12px; border-radius: var(--r-ctl); border: 1px solid var(--edge);
    background: var(--raised); color: var(--text); font-family: var(--program); font-size: 15px; outline: none; caret-color: var(--catch);
    transition: border-color var(--m-base) var(--ease-house), box-shadow var(--m-base) var(--ease-house);
  }
  input:focus, select:focus, textarea:focus { border-color: var(--catch-line); box-shadow: 0 0 0 3px var(--catch-wash); }
  input::placeholder, textarea::placeholder { color: var(--text-faint); }
  textarea { resize: vertical; }
  button:not([class*="pp-"]):not(.sheet-tab):not(.ppctx-item):not(.bke-btn):not(.pp-seg *):not(.pp-rungs *):not(.rungs *):not(.pp-tile *):not(.way):not(.ed-tbtn):not(.pt-formats *) {
    display: inline-flex; align-items: center; justify-content: center; gap: 7px; cursor: pointer;
    min-height: var(--ctl-sm); padding: 0 16px; border-radius: var(--r-ctl); border: 1px solid var(--key); background: var(--key); color: var(--on-key);
    font-family: var(--marquee); font-weight: 500; font-size: var(--t-cta); letter-spacing: var(--trk-cta); line-height: 1;
    transition: background var(--m-base) var(--ease-house), border-color var(--m-base) var(--ease-house), color var(--m-base) var(--ease-house), transform var(--m-fast) var(--ease-house), opacity var(--m-fast) var(--ease-house);
  }
  button:not([class*="pp-"]):not(.pp-seg *):not(.pp-rungs *):not(.rungs *):not(.pp-tile *):not(.way):not(.ed-tbtn):not(.pt-formats *):hover { background: var(--key-hi); }
  button:not([class*="pp-"]):not(.pp-seg *):not(.pp-rungs *):not(.rungs *):not(.pp-tile *):not(.way):not(.ed-tbtn):not(.pt-formats *):active { transform: scale(.97); opacity: .72; }
  button:not([class*="pp-"]):not(.pp-seg *):not(.pp-rungs *):not(.rungs *):not(.pp-tile *):not(.way):not(.ed-tbtn):not(.pt-formats *):disabled { opacity: .4; cursor: not-allowed; transform: none; }
  button.ghost, button.small.ghost { background: var(--glass); color: var(--text); border-color: rgba(255, 255, 255, .07); }
  button.ghost:hover { background: var(--glass); border-color: rgba(255, 255, 255, .14); }
  button.danger { background: transparent; color: var(--error); border-color: rgba(255, 77, 77, .45); }
  button.danger:hover { background: var(--error-quiet); }
  button.small { min-height: 36px; padding: 0 12px; font-size: 14px; }
  .switch { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); padding: var(--s-2) 0; }
  .detail-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-top: var(--s-2); }
  .secret { font-family: var(--program); font-variant-numeric: tabular-nums; font-size: 14px; background: var(--raised); border: 1px dashed var(--edge); border-radius: var(--r-sm); padding: 10px 12px; margin: 6px 0; word-break: break-all; }
  .pill { display: inline-flex; align-items: center; gap: 5px; font-family: var(--marquee); font-weight: 500; font-size: var(--t-chip); letter-spacing: var(--trk-chip); text-transform: uppercase; color: var(--text-dim); }
  .pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--text-faint); }
  .pill.live { color: var(--text); } .pill.live::before { background: var(--catch); }
  .pill.role { color: var(--text-dim); }

  /* consumers: components.css names on unported pages (dashboard, events, account, billing, shop, checkout). */
  .ts-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: var(--ctl-md); padding: 0 22px; border-radius: var(--r-ctl); border: 1px solid transparent; cursor: pointer; background: var(--glass); color: var(--text); font-family: var(--marquee); font-weight: 500; font-size: 17px; letter-spacing: var(--trk-cta); line-height: 1; text-decoration: none; transition: background var(--m-base) var(--ease-house), transform var(--m-fast) var(--ease-house), opacity var(--m-fast) var(--ease-house); }
  .ts-btn:active { transform: scale(.97); opacity: .72; }
  .ts-btn--primary { background: var(--key); color: var(--on-key); box-shadow: var(--sh-key); }
  .ts-btn--primary:hover { background: var(--key-hi); }
  .ts-btn--ghost { border-color: rgba(255, 255, 255, .07); }
  .ts-btn:disabled { opacity: .4; pointer-events: none; }
  .ts-btn.is-loading { position: relative; color: transparent !important; pointer-events: none; }
  .ts-btn.is-loading::after { content: ""; position: absolute; width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--on-key); border-top-color: transparent; animation: pp-spin .7s linear infinite; }
  .tsn-cta { display: inline-flex; align-items: center; justify-content: center; min-height: var(--ctl-md); padding: 0 22px; border-radius: var(--r-ctl); background: var(--key); color: var(--on-key); font-family: var(--marquee); font-weight: 500; font-size: 17px; letter-spacing: var(--trk-cta); text-decoration: none; border: 0; cursor: pointer; }
  .tsn-kicker { font-family: var(--marquee); font-weight: 500; font-size: var(--t-kicker); letter-spacing: var(--trk-kicker); text-transform: uppercase; color: var(--text-dim); }
  .tsn-display, .tsn-display--section { font-family: var(--marquee); font-weight: 500; font-size: var(--t-room); letter-spacing: var(--trk-room); text-transform: uppercase; color: var(--text); margin: 0; }
  .ts-field { display: block; width: 100%; min-height: var(--ctl-md); padding: 12px 14px; background: var(--raised); color: var(--text); border: 1px solid var(--edge); border-radius: var(--r-ctl); font-size: 16px; outline: none; }
  .ts-field:focus { border-color: var(--catch-line); box-shadow: 0 0 0 3px var(--catch-wash); }
  .ts-label { display: block; font-family: var(--marquee); font-weight: 500; font-size: var(--t-kicker); letter-spacing: var(--trk-kicker); text-transform: uppercase; color: var(--text-dim); margin: var(--s-3) 0 6px; }
  .ts-card { background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r-card); padding: 18px; }
  .ts-avatar { width: 32px; height: 32px; border-radius: 50%; background: var(--raised); box-shadow: inset 0 0 0 1px var(--line2); display: inline-flex; align-items: center; justify-content: center; font-family: var(--marquee); font-weight: 500; font-size: 13px; color: var(--text); flex: none; }
  .ts-nums { font-variant-numeric: tabular-nums; }

  /* consumers: the create/claim sheet partial (templates/create-sheet.html + create-sheet.js). */
  .ts-sheet-scrim { position: fixed; inset: 0; z-index: var(--z-sheet); display: flex; align-items: flex-end; justify-content: center; background: rgba(0, 0, 0, .5); animation: pp-fade var(--m-base) var(--ease-house) both; }
  .ts-sheet-scrim[hidden] { display: none; }
  .ts-sheet { position: relative; width: 100%; animation: pp-sheet-up var(--spring-deck-dur) var(--spring-deck) both; }
  .sheet-aura { display: none; }
  .sheet-inner { position: relative; background: var(--panel); border: 1px solid var(--line2); border-radius: var(--r-sheet) var(--r-sheet) 0 0; box-shadow: var(--sh-sheet); padding: 10px 20px calc(24px + var(--safe-b)); max-height: 82vh; overflow-y: auto; overscroll-behavior: contain; }
  .ts-sheet-handle { width: 40px; height: 5px; border-radius: 3px; background: var(--raised); margin: 6px auto 14px; }
  .sheet-title { font-family: var(--marquee); font-weight: 500; font-size: var(--t-sheet); margin: 4px 0 var(--s-3); }
  .sheet-tabs { position: relative; display: grid; grid-template-columns: 1fr 1fr; padding: 4px; border-radius: var(--r-ctl); background: var(--glass); margin: 0 0 var(--s-4); }
  .sheet-tab { position: relative; min-height: 40px; border: 0; background: none; cursor: pointer; font-family: var(--program); font-weight: 600; font-size: 14.5px; color: var(--text-dim); border-radius: var(--r-sm); transition: color var(--m-base) var(--ease-house), background var(--m-base) var(--ease-house); }
  .sheet-tab.active { color: var(--text); background: var(--raised); box-shadow: var(--sh-thumb); }
  .sheet-pane[hidden] { display: none; }
  @media (min-width: 720px) {
    .ts-sheet-scrim { align-items: center; padding: 24px; }
    .ts-sheet { max-width: 520px; animation: pp-sheet-grow var(--m-cine) var(--ease-cine) both; }
    .sheet-inner { border-radius: var(--r-sheet); }
    .ts-sheet-handle { display: none; }
  }

  /* (the dashboard's KPI strip, onboard card and polaroid grid were ported in Phase 1; their compat rules went with them) */

  /* consumers: event.html widgets + charts.js until Phase 2; billing/franchise ledgers until Phase 3. */
  .widget-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--s-4); }
  .widget-card { grid-column: span 12; background: var(--panel); border: 1px solid var(--edge); border-radius: var(--r-card); padding: 16px; }
  .widget-card > h3 { font-family: var(--marquee); font-weight: 500; font-size: var(--t-eyebrow); letter-spacing: var(--trk-eyebrow); text-transform: uppercase; color: var(--text-faint); margin: 0 0 12px; }
  .widget-figure { font-family: var(--marquee); font-weight: 500; font-size: 34px; line-height: 1; font-variant-numeric: tabular-nums; }
  .widget-sub { font-size: var(--p-foot); color: var(--text-dim); margin-top: 6px; }
  .widget-center { display: flex; flex-direction: column; align-items: center; text-align: center; }
  @media (min-width: 720px) { .w-3 { grid-column: span 3; } .w-4 { grid-column: span 4; } .w-5 { grid-column: span 5; } .w-6 { grid-column: span 6; } .w-7 { grid-column: span 7; } .w-8 { grid-column: span 8; } }
  .pp-chart { display: block; width: 100%; }
  .pp-spark { height: 34px; } .pp-area, .pp-bars { height: 120px; }
  .pp-ring { width: 132px; height: 132px; margin: 4px auto 2px; }
  .pp-gauge { width: 150px; height: 96px; margin: 0 auto; overflow: visible; }
  .pp-ring-num { font-family: var(--marquee); font-size: 21px; } .pp-ring-sub { font-size: 10px; }
  .pp-capbar { height: 5px; border-radius: 999px; background: var(--well); overflow: hidden; }
  .pp-capbar-fill { display: block; height: 100%; border-radius: 999px; transition: width var(--m-base) var(--ease-house); }
  .top-photos { display: flex; gap: 8px; flex-wrap: wrap; }
  .top-photos .tp { position: relative; width: 72px; height: 72px; border-radius: var(--r-xs); overflow: hidden; background: var(--raised); flex: none; }
  .top-photos .tp img { width: 100%; height: 100%; object-fit: cover; }
  .top-photos .tp .hc { position: absolute; left: 4px; bottom: 4px; font-size: 11px; font-weight: 700; color: #fff; background: rgba(0, 0, 0, .58); border-radius: 5px; padding: 1px 6px; }
  .ent-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s-3); }
  .ent-item { padding: 10px 0; border-top: 1px solid var(--edge); }
  .ent-item .k { font-family: var(--marquee); font-weight: 500; font-size: var(--t-chip); letter-spacing: .18em; text-transform: uppercase; color: var(--text-faint); }
  .ent-item .v { font-size: 14px; font-weight: 600; margin-top: 3px; color: var(--text); }
  .ent-item .v.off { color: var(--text-faint); }
  .queue { display: flex; flex-direction: column; margin-top: var(--s-2); }
  .qitem { display: grid; grid-template-columns: auto 1fr auto; gap: var(--s-3); align-items: center; padding: 11px 0; border-top: 1px solid var(--edge); }
  .qitem img { width: 64px; height: 64px; border-radius: var(--r-sm); object-fit: cover; flex: none; cursor: pointer; }
  .qitem .qmeta { min-width: 0; font-size: var(--p-hint); color: var(--text-dim); }
  .qitem .qbtns { display: flex; gap: 6px; flex: none; }
  .orders-table { width: 100%; border-collapse: collapse; font-size: 14px; margin-top: 4px; }
  .orders-table th, .orders-table td { text-align: left; padding: 10px 10px 10px 0; border-bottom: 1px solid var(--edge); }
  .orders-table th { font-family: var(--marquee); font-weight: 500; font-size: var(--t-eyebrow); letter-spacing: var(--trk-eyebrow); text-transform: uppercase; color: var(--text-faint); }
  .orders-table td { font-variant-numeric: tabular-nums; }
  .orders-table td.r, .orders-table th.r { text-align: right; padding-right: 0; }
  .orders-table tr:last-child td { border-bottom: 0; }
  .share-row { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; margin-top: var(--s-4); }
  .share-qr { background: #fff; border-radius: var(--r-sm); padding: 12px; flex: none; line-height: 0; }
  .share-body { min-width: 220px; flex: 1; }
  .designer { display: grid; grid-template-columns: 1fr; gap: 20px; margin-top: var(--s-4); }
  @media (min-width: 760px) { .designer { grid-template-columns: 1fr 280px; } }
  .mini-poster { background: var(--p-bg, #fff); color: var(--p-fg, #111); border-radius: var(--r-sm); padding: 22px 18px; text-align: center; }

  @media (max-width: 620px) {
    .row { flex-direction: column; align-items: stretch; }
    .row > div, .row > div[style] { max-width: none !important; width: 100%; }
  }
}

/* ============================================================ paper ======= */
/* THE SHOP IS PAPER (2026-09-08, the owner: "white with black dots, no matter the applied
 * theme"). The shop browses and pays on white: the printed room inverted, its halftone in
 * black ink, whatever colourway the console wears elsewhere (the dark stores go light at the
 * point of sale; the only controlled evidence on dark checkouts points to lower trust). A
 * paper page puts .pp-paper on <html> before first paint (an inline script in its headExtra);
 * room.js reads --ground / --screen-dot like any other way, so the canvas prints black dots on
 * white by itself. UNLAYERED on purpose: room.css's :root and :root[data-way] token blocks are
 * unlayered, and a layered override would lose to them whatever its specificity. Every
 * component inverts for free because it reads tokens; the rules below are the ones that named
 * a colour. */
html.pp-paper {
  color-scheme: light;
  --void: #FFFFFF; --panel: #FFFFFF; --raised: #F1F1F3; --edge: #DCDCE0; --line2: #E6E6EA; --well: #E6E6EA;
  --text: #131316; --text-dim: #5C5C66; --text-faint: #8A8A94;
  --glass: rgba(255, 255, 255, .8); --glass-raised: rgba(255, 255, 255, .9);
  --catch: #131316; --catch-line: rgba(19, 19, 22, .42); --catch-wash: rgba(19, 19, 22, .08);
  --key: #131316; --key-hi: #2A2A30; --key-lo: #000000; --on-key: #FFFFFF;
  --ground: #FFFFFF; --screen-dot: #131316; --screen-dot-2: none;
  --sh-rail: none;
  --sh-key: 0 6px 12px rgba(19, 19, 22, .22); --sh-poster: 0 6px 15px rgba(19, 19, 22, .16); --sh-thumb: 0 1px 4px rgba(19, 19, 22, .18); --sh-sheet: 0 8px 18px rgba(19, 19, 22, .18);
}
html.pp-paper body { background: var(--ground); }
/* Paper has no room: the canvas is gone before any script runs (room.js also never draws it). */
html.pp-paper .pp-room { display: none; }
/* The lit key becomes an INK key: the same coat, the same pointer light at lower gain. */
html.pp-paper .pp-btn--key {
  background:
    radial-gradient(240px circle at calc(100% - var(--gx)) calc(105% - var(--gy)), rgba(0,0,0,.35), transparent 60%),
    radial-gradient(220px circle at var(--gx) var(--gy), rgba(255,255,255,.22), rgba(255,255,255,.08) 26%, transparent 54%),
    linear-gradient(180deg, #2E2E34 0%, #131316 48%, #000000 100%);
}
html.pp-paper .pp-btn--key::before { background: rgba(255,255,255,.14); }
html.pp-paper .pp-btn--key::after { background: rgba(0,0,0,.5); }
html.pp-paper .pp-btn--key:active, html.pp-paper .pp-btn--key.is-down { box-shadow: var(--sh-key), inset 0 0 0 100vmax rgba(255,255,255,.12); }
html.pp-paper .pp-btn--ghost { border-color: rgba(19, 19, 22, .16); }
html.pp-paper .pp-btn--ghost::before { background: linear-gradient(90deg, transparent, rgba(19,19,22,.35) var(--gx), transparent); }
html.pp-paper .pp-switch { box-shadow: inset 0 0 0 1px rgba(19, 19, 22, .16); }
html.pp-paper .pp-switch:checked { box-shadow: inset 0 0 0 1px rgba(19, 19, 22, .28); }
html.pp-paper .pp-poster::after { box-shadow: inset 0 0 0 1px rgba(19, 19, 22, .10); }
html.pp-paper .pp-kpi-label { color: rgba(19, 19, 22, .7); }
html.pp-paper .pp-skel::after { background: linear-gradient(90deg, transparent, rgba(19, 19, 22, .05), transparent); }
html.pp-paper .skip-link { background: var(--key); color: var(--on-key); }
html.pp-paper ::selection { background: var(--catch-wash); }
/* The edgeless ground is the paper itself; the rail's phone bar is white glass. */
html.pp-paper .edgeless::before, html.pp-paper .pp-notice::before, html.pp-paper .ts-toast::before { background: var(--ground); opacity: .94; }
html.pp-paper .pp-hud-mark .pp-face { color: var(--text); }
