/* =========================================================================
   INSTALL · DESIGN RAIL — lifted CSS (Lane 2a, Sep 2026)
   Single source: css/install-design.css. This file is LIFTED, byte-for-byte,
   from design-directions/install-design-rail.html's <style> block
   (lines 13-313) per the Lane 2a brief. Rule selection: every rule whose
   selector starts with `.pd-` (which covers `.pd-field`) is kept, in the
   mock's own order; dropped are (a) rules that only restyle `.pr-*` classes
   — practice.css owns those — and (b) the `.pd-devices` device-frame toggle
   and every `html.ipad *` rule (a mock-only affordance for switching the
   demo's device frame, per the brief). No value in a kept rule is rewritten.

   TWO DELIBERATE DEPARTURES FROM THE MECHANICAL LIFT (both reported in
   .superpowers/sdd/install-design-rail/briefs/lane2a-report.md):

   1. The `:root` token block (mock:24-40) is included even though its own
      selector is `:root`, not `.pd-`. Every `.pd-*` rule below references a
      `--pd-*` custom property defined ONLY here (grep confirms no other
      shared CSS file defines them) — omitting it would leave every var()
      unresolved and the surface would paint with no strokes at all.

   2. `.pd-field .pd-play { display: none; }` and its companion
      `.pd-field[data-var="N"] .pd-play[data-var="N"] { display: block; }`
      (mock's own toggle for switching between several SAVED PLAY
      VARIATIONS baked side-by-side into one SVG) are NOT lifted. js/install-
      design-svg.js's `.pd-play` group carries `data-option`, never
      `data-var` — it is ballOptions (in-play option branches), a different
      concept from the mock's variations strip. Lifting the toggle verbatim
      would hide the surface's one-and-only `.pd-play` group by default with
      nothing to ever match `[data-var]` and turn it back on.

   ADDITIONS (beyond the lift) are marked ADDITION below, each with why.

   ── TASK 1 (Sep 2 2026): THE v2 CHROME ────────────────────────────────────
   The CHROME around the board is now lifted from
   design-directions/play-designer-v2.html's <style> (mock:9-294) per the
   Task-1 brief: the mock's own class prefix becomes `.pd-`, and its
   `--on-ink` becomes `--pd-on-ink`. Four
   departures from a mechanical rename, each forced and each listed here:

   1. The mock's `.board .*` block (mock:152-216) is NOT lifted. This surface
      is `.pd-field`, painted by js/install-design-svg.js, whose class
      vocabulary the brief forbids changing; `.board` exists nowhere in this
      page, so every one of those rules would be dead CSS asserting a second,
      contradictory board. The BOARD PALETTE TOKENS the block reads
      (--board-*, --route-*, --tok-*) ARE lifted — Task 3 paints with them.
   2. The mock's `:root[data-theme="dark"]` SHELL palette (mock:56-61,
      --canvas/--surface/--ink/...) is dropped: css/practice.css:56 owns that
      ladder for the whole Install page. Only `--seg-accent` (which practice
      .css does not define) survives from that block, plus a night value for
      --pd-chalk/--pd-chalk-strong, which were hardcoded DARK ink and would
      have painted an invisible board the moment the new Night tab was used.
   3. Five mock class names would collide with names this file already owns,
      so each mock rule is applied to THIS file's existing name instead of
      introducing a duplicate: the mock's `top` -> `.pd-topbar`, its `id` ->
      `.pd-titleblock`, its `sub` -> `.pd-title-sub`, its `actions` ->
      `.pd-topbar-actions` (js/install-design.js:810 queries that last one),
      and its `foot-hint` -> `.pd-contract` (the brief's own map). The mock's
      `field` (a label-plus-control block) becomes `.pd-field-block`, NEVER
      `.pd-field` — on this page `.pd-field` is the SVG board itself.
   4. `.pr-chip.active { color: var(--on-ink) }` (mock:62) is dropped: it
      restyles a shell class used by Concepts and Drills on this same page,
      and this file's lift deliberately carries no `.pr-*` rules.
   ========================================================================= */

    :root {
        --pd-paper: var(--surface);
        --pd-chalk: rgba(13, 18, 32, 0.14);
        --pd-chalk-strong: rgba(13, 18, 32, 0.34);
        --pd-los: var(--ink);
        --pd-route: var(--ink);            /* a pass route */
        --pd-motion: var(--volt);          /* pre-snap motion, dashed — VOLT (Carlos, Sep 3 2026) */
        --pd-ball: var(--gold);            /* the ball's path */
        --pd-defense: var(--live);         /* the look, ghosted */
        --pd-tree: var(--ink-faint);       /* the tree, until chosen */
        --pd-select: var(--volt);
        --pd-on-ink: var(--on-ink);       /* text on an ink fill: surface, so it flips with the theme */
        --pd-strip-hover: color-mix(in srgb, var(--surface) 10%, transparent);
        --pd-strip-line: color-mix(in srgb, var(--surface) 16%, transparent);
        --pd-shadow: 0 6px 18px rgba(17, 19, 22, 0.16);
        --pd-shadow-lift: 0 10px 28px rgba(17, 19, 22, 0.18);

        /* ── the BOARD's own palette (mock:33-42), lifted. Task 3 paints the
           kind x cap markers from it; the Kind swatches read it today. ── */
        --board-turf-a: #264c28; --board-turf-b: #316934; --board-turf-c: #3e7842;
        --board-chalk: rgba(255,255,255,0.28); --board-chalk-strong: rgba(255,255,255,0.7);
        --board-los: #fbbf24; --board-ez: #1c3a1e;
        --route-pass: #ffffff; --route-run: #fbbf24; --route-pa: #3b82f6;
        --ball: #fbbf24; --defense: #ef4444;
        /* Legend swatches only (.pd-tok rules below) -- the live install.html
           board paints Q/C via --info/--gold, not these. Kept equal to
           css/play-designer-v2.css's [data-tokens="colour"] C/X/Y/Z/H and
           js/play-renderer.js's TOKEN_HUES (Oct 1 2026 standard flag/7v7
           convention) so the legend never shows a palette the board
           disagrees with -- proven by scripts/test-position-palette-parity.js. */
        --tok-Q: #ef4444; --tok-C: #737373; --tok-X: #2563eb; --tok-Y: #16a34a; --tok-Z: #dc2626; --tok-H: #db7706;
        --tok-ink: #ffffff;
        --select: var(--volt);
        --read: var(--volt);                        /* the defender an option keys on */

        /* v1's segmented switch (mock:67): an INK plate in both themes. */
        --pm-ink: #0b0c0e; --pm-ink-raised: #24282d; --pm-ink-text: #9aa1ab; --pm-ink-text-hover: #c9cdd3; --pm-ink-text-active: #ffffff;
    }
    /* DAY: the same play as paper — ink lines on white, the card the wristband
       prints (mock:44-53). Colour stays meaning. */
    :root:not([data-theme="dark"]) {
        --board-turf-a: #ffffff; --board-turf-b: #ffffff; --board-turf-c: #ffffff;
        --board-chalk: rgba(13,18,32,0.14); --board-chalk-strong: rgba(13,18,32,0.34);
        --board-los: #0d1220; --board-ez: #e9e9e9;
        --route-pass: #0d1220; --route-run: #eab308; --route-pa: #2563eb;
        --ball: #d97706; --defense: #d92d20;
        --read: #0d1220;                            /* volt is a fill, never text on white: the read is ink by day */
        --seg-accent: var(--volt);
    }
    /* NIGHT: css/practice.css:56 owns the shell ladder (departure 2 above).
       What is left is the seg's own accent, plus the two chalk values that
       were hardcoded DARK ink — on a night surface they painted nothing. */
    :root[data-theme="dark"] {
        --seg-accent: #22c55e;                      /* v1's green underline stays v1's at night */
        --pd-chalk: rgba(242, 245, 250, 0.16);
        --pd-chalk-strong: rgba(242, 245, 250, 0.40);
    }

    /* ── v1's segmented switch, lifted (mock:68-79): an INK plate in both
       themes, the active tab RAISED with the accent underline. ONE grammar
       for the head, the board foot, the header and the inspector. ────────── */
    .seg { display: inline-flex; gap: 2px; background: var(--pm-ink); padding: 3px; border-radius: 8px; box-shadow: inset 0 0 6px rgba(0,0,0,0.7); }
    .seg-tab { position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 6px; background: transparent; border: 0; color: var(--pm-ink-text); padding: 0 12px; min-height: 36px; border-radius: 6px; font-family: var(--font-ui); font-weight: 700; font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; cursor: pointer; transition: color 0.18s ease, background 0.18s ease; white-space: nowrap; }
    .seg-tab:hover { color: var(--pm-ink-text-hover); }
    .seg-tab.active { background: linear-gradient(180deg, var(--pm-ink-raised) 0%, var(--pm-ink) 100%); color: var(--pm-ink-text-active); box-shadow: inset 0 1px 0 rgba(255,255,255,0.08), 0 3px 8px rgba(0,0,0,0.4); }
    .seg-tab.active::after { content: ""; position: absolute; bottom: 2px; left: 24%; right: 24%; height: 2px; border-radius: 2px; background: var(--seg-accent); box-shadow: 0 0 8px var(--seg-accent); }
    .seg-tab svg { width: 28px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
    .seg-tab svg polygon, .seg-tab svg circle { fill: currentColor; }
    .seg-tab svg .open { fill: none; }
    .seg-tab i svg { width: 15px; height: 15px; }
    .seg.wide { display: flex; } .seg.wide .seg-tab { flex: 1; }
    .seg.sm .seg-tab { min-height: 32px; padding: 0 10px; font-size: 11px; }
    .seg-tab .pd-swatch { box-shadow: 0 0 0 2px var(--board-turf-a); }   /* the colour ON its board */
    .pd-swatch { width: 10px; height: 10px; border-radius: 50%; margin-right: 6px; }

    .pd-main { display: grid; grid-template-rows: auto 1fr auto; gap: 12px; height: 100%; min-height: 0; }

    /* ── TOP STRIP: who · what · one status · one action (mock:85-112) ───── */
    .pd-topbar { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 16px; min-height: 52px; }
    .pd-titleblock { display: flex; align-items: center; gap: 14px; min-width: 0; }
    .pd-back { width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-xs); color: var(--ink-muted); border: 1px solid var(--border); background: var(--surface); flex: none; }
    .pd-back:hover { color: var(--ink); background: var(--hover); }
    .pd-back svg { width: 18px; height: 18px; }
    .pd-name { min-width: 0; }
    .pd-name input { width: 100%; max-width: 420px; background: transparent; border: 0; border-bottom: 1px solid transparent; color: var(--ink); font-family: var(--font-display); font-size: 26px; font-weight: 800; line-height: 1.05; text-transform: uppercase; letter-spacing: 0.4px; padding: 0; outline: none; }
    /* ⛔ THE UNDERLINE IS RESTORED, BECAUSE THE RENAME NOW HAPPENS. The note
       that stood here said the mock's `:hover`/`:focus` underline was withheld
       while #pdTitle shipped `readonly` — "a hover border and a volt focus
       rule promise a rename no code performs" — and pointed at the task that
       would wire it. That task is this branch (51b83096a): the field saves
       plays.name through flushAutosave's `notes` pattern, so withholding the
       affordance is now the lie, and the note describing a readonly field the
       file no longer has is deleted rather than left standing. */
    .pd-name input:hover { border-bottom-color: var(--border); }
    .pd-name input:focus { border-bottom-color: var(--volt); }
    .pd-name input::placeholder { color: var(--ink-faint); }
    /* the what is one sentence (Rajdhani, sentence case), never a row of chips */
    .pd-title-sub { font-size: 14px; font-weight: 600; color: var(--ink-muted); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .pd-title-sub b { font-weight: 700; color: var(--ink); }
    .pd-history { display: flex; gap: 4px; }
    .pd-topbar-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }
    .pd-saved { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 700; color: var(--ink-faint); }
    .pd-saved svg { width: 14px; height: 14px; }
    /* ⛔ THE CLASS ARRIVED WITH NO RULE, ON BOTH LINES. `.pd-why` came in with
       51b83096a's markup and is styled in NEITHER this file nor any other on
       preview/designer-v2 — a button with no rule paints as the browser's own
       grey chrome in the middle of the strip, which is the invented-class
       failure the platform rules name by name. It is a quiet chip beside the
       status it explains, in the strip's own grammar: `.pd-saved`'s type
       scale, `.pd-back`'s border and hover. Hidden until there IS a reason,
       so the strip is unchanged on the happy path. */
    .pd-why { display: inline-flex; align-items: center; gap: 5px; min-height: 28px; padding: 0 9px; border-radius: var(--r-xs); border: 1px solid var(--border); background: var(--surface); color: var(--ink-muted); font-size: 12px; font-weight: 700; }
    .pd-why:hover { background: var(--hover); color: var(--ink); }
    /* ⛔ `display:inline-flex` above BEATS the UA's [hidden] rule, so setSaveReason's
       `why.hidden = true` (js/install-design.js:1024) never took it off the strip —
       "Saved" and "Why" sat side by side on the happy path. Found on the Sep 24 walk. */
    .pd-why[hidden] { display: none; }
    .pd-why svg { width: 13px; height: 13px; }
    .pd-topbar .pr-btn { min-height: 40px; }

    /* one icon button, everywhere: 40px, 8px radius, ink when on (mock:103-109).
       The old `.pd-icons` wrapper rule is gone with its markup: the v2 chrome
       groups icons with `.pd-group` / `.pd-history`. */
    .pd-icon { width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-xs); color: var(--ink-muted); border: 1px solid transparent; }
    .pd-icon:hover { background: var(--hover); color: var(--ink); }
    .pd-icon.on { background: var(--ink); color: var(--pd-on-ink); box-shadow: inset 0 -2px 0 var(--volt); }
    .pd-icon:disabled { color: var(--ink-faint); opacity: 0.6; }
    .pd-icon svg { width: 18px; height: 18px; }
    .pd-seg-icons .seg-tab { min-height: 40px; padding: 0 12px; gap: 6px; }
    .pd-seg-icons .seg-tab svg { width: 15px; height: 15px; }

    /* ── BENCH: board card + inspector (mock:115-131) ────────────────────── */
    .pd-bench { display: grid; grid-template-columns: 1fr 336px; gap: 12px; min-height: 0; }
    .pd-canvas { position: relative; display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-card); min-height: 0; overflow: hidden; }
    .pd-canvas-head, .pd-canvas-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 12px; min-height: 52px; }
    .pd-canvas-head { border-bottom: 1px solid var(--border); }
    .pd-canvas-head .seg-tab, .pd-canvas-foot .seg-tab { min-height: 36px; }
    .pd-canvas-foot .seg-tab:not(:has(.pd-swatch)) { padding: 0 9px; }   /* glyph tabs: the glyph is the width */
    .pd-group { display: flex; align-items: center; gap: 8px; }
    .pd-group .pr-label { margin-right: 2px; }
    .pd-foot-groups { display: flex; align-items: center; justify-content: space-between; gap: 14px; width: 100%; }
    .pd-stage { position: relative; flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; padding: 14px 14px 14px 78px; background: var(--surface-elevated); }
    /* ⛔ AN SVG NEEDS A DEFINITE SIZE, NOT A STRETCH (Sep 3 2026 — Carlos on an
       iPad: "the canvas is invisible. On desktop I can see it but not here").
       #pdField carries a viewBox and NO width/height attributes, so it is a
       replaced element with an intrinsic RATIO but no intrinsic SIZE. This
       rule sized it by `align-self: stretch` + `height: auto` — which Chrome
       resolves and Safari does not: Safari declines to stretch a replaced
       element that already has a ratio, leaving nothing for the width to be
       derived from, and the board paints at zero. His screenshot proves the
       stage itself HAS height (its surface renders); only the svg inside it
       was gone.
       Now it starts from the WIDTH, which is always definite, lets the ratio
       give the height, and caps the height so it still contains. Reported
       twice before it was found — the first time (the v2 mock, same rule at
       play-designer-v2.html:132) I blamed portrait layout and moved on. */
    .pd-stage svg.pd-field { width: 100%; height: auto; max-width: 100%; max-height: 100%; aspect-ratio: 400 / 302; }
    .pd-stage svg.pd-field[data-window="board"] { width: 100%; aspect-ratio: 3 / 2; }   /* width restated so this rule carries its own definite size: an svg sized by a viewBox alone is a ratio with no size, and WebKit paints it at ZERO WIDTH (scripts/audit-svg-sizing.js, lane SVGW Oct 12 2026) */   /* SC_BOARD_GEOMETRY.aspect — coach/install.html's board is the ONE board too (lane DSG-TILES, Oct 11 2026; play-designer-v2.css carries the same rule for the designer) */
    .pd-field .sideline { stroke: var(--pd-chalk-strong); stroke-width: 2; }
    /* the tool rail: what you draw. Fixed on the board's left edge. 44px targets. */
    .pd-tools { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: 2px; padding: 4px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); }
    .pd-tool { position: relative; width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-xs); color: var(--ink-muted); }
    .pd-tool:hover { color: var(--ink); background: var(--hover); }
    .pd-tool.on { color: var(--pd-on-ink); background: var(--ink); box-shadow: inset 3px 0 0 var(--volt); }
    .pd-tool:disabled { color: var(--ink-faint); opacity: 0.45; cursor: default; }
    .pd-tool:disabled:hover { background: none; color: var(--ink-faint); }
    .pd-tool svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
    .pd-tool[data-tip]:hover::after { content: attr(data-tip); position: absolute; left: 52px; top: 50%; transform: translateY(-50%); white-space: nowrap; background: var(--ink); color: var(--pd-on-ink); font-size: 12px; font-weight: 700; padding: 5px 9px; border-radius: 6px; pointer-events: none; z-index: 2; }
    .pd-tools .pr-tool-divider { height: 1px; width: auto; align-self: auto; background: var(--border); margin: 4px 6px; }
    .pd-field text { font-family: var(--font-data); font-size: 11px; fill: var(--pd-chalk-strong); }
    .pd-field .chalk { stroke: var(--pd-chalk); stroke-width: 1; }
    .pd-field .hash { stroke: var(--pd-chalk); stroke-width: 1; opacity: 0.85; }                /* one tick per yard: at both sidelines and on two inner hash columns, so a depth can be counted, not guessed */
    /* the sidelines (Sep 24 2026): solid chalk at the format's width, the ground
       outside them shaded out of bounds — js/install-design-svg.js buildSidelines */
    .pd-field .sidelines { pointer-events: none; }
    .pd-field .sidelines .sideline { stroke: var(--pd-los); stroke-width: 2; stroke-opacity: 0.8; }
    .pd-field .sidelines .oob { fill: var(--pd-los); fill-opacity: 0.07; }
    .pd-field .los { stroke: var(--pd-los); stroke-width: 2.5; transition: stroke 120ms linear, stroke-width 120ms linear; }
    /* The wall says so when it refuses (playmaker.js:5474's own rule): a drag
       that stops with no explanation reads as a broken board, not as a law. */
    .pd-field .los.denied { stroke: var(--live); stroke-width: 4; }
    .pd-field .tok { fill: var(--pd-paper); stroke: var(--pd-route); stroke-width: 2; cursor: pointer; }
    .pd-field .tok-label { font-family: var(--font-ui); font-weight: 800; font-size: 12px; fill: var(--ink); text-anchor: middle; dominant-baseline: central; }
    .pd-field.filled .tok:not(.ghost) { fill: var(--ink); }
    .pd-field.filled .tok-label:not(.ghost) { fill: var(--pd-on-ink); }
    .pd-field.filled .tok[data-pos="Q"]:not(.ghost) { fill: var(--info); stroke: var(--info); }
    .pd-field.filled .tok[data-pos="C"]:not(.ghost) { fill: var(--gold); stroke: var(--gold); }
    .pd-field.filled .tok-label[data-pos="C"] { fill: var(--ink); }
    .pd-field.filled .ball { stroke: var(--ink); stroke-width: 1.2; }
    .pd-field .tok-label, .pd-field .sel, .pd-field .sel-halo, .pd-field .read, .pd-field .read-n { pointer-events: none; }  /* the tap lands on the player, never on his letter or his ring */
    .pd-field .tok.ghost { pointer-events: none; }   /* the ghost is a picture of where he lands, never a thing to tap */
    .pd-field .ball { fill: var(--pd-ball); stroke: none; }
    .pd-field .route { fill: none; stroke: var(--pd-route); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; marker-end: url(#pdArrow); pointer-events: none; }   /* the tap lands on the wide hit path beneath it */
    .pd-field .route[data-weight="s"], .pd-field .rgroup[data-weight="s"] .route { stroke-width: 1.6; }
    .pd-field .route[data-weight="l"], .pd-field .rgroup[data-weight="l"] .route { stroke-width: 4; }
    .pd-field .route[data-end="chevron"], .pd-field .rgroup[data-end="chevron"] .route:not(.stem) { marker-end: url(#pdEndChevron); }
    .pd-field .route[data-end="block"], .pd-field .rgroup[data-end="block"] .route:not(.stem) { marker-end: url(#pdEndBlock); }
    .pd-field .route[data-end="dot"],   .pd-field .rgroup[data-end="dot"]   .route:not(.stem) { marker-end: url(#pdEndDot); }
    .pd-field .route[data-end="hitch"], .pd-field .rgroup[data-end="hitch"] .route:not(.stem) { marker-end: url(#pdEndHitch); }
    .pd-field .route[data-end="curl"],  .pd-field .rgroup[data-end="curl"]  .route:not(.stem) { marker-end: url(#pdEndCurl); }
    .pd-field .route-hit { fill: none; stroke: transparent; stroke-width: 22; stroke-linecap: round; stroke-linejoin: round; pointer-events: stroke; cursor: pointer; }   /* 44px of route to tap, and the selection halo when it is */
    .pd-field .route-hit.on { stroke: var(--pd-select); opacity: 0.16; }
    .pd-field.running .route-hit { pointer-events: none; }
    /* ⛔ THE MOTION IS A LEG, NOT THE LINE. `.route.motion` coloured the WHOLE
       path, so a man who motioned and then ran a route drew all of it in the
       motion colour. The pre-snap head is now its own overlay drawn on top of
       the ordinary black route, so the motion reads volt and everything after
       the snap reads like every other player's line. It carries no marker: the
       arrowhead belongs to the END of the route, not to the end of the motion. */
    /* `.route.motion` is GONE with the class that set it: it coloured the whole
       line and gave the motion its own arrowhead, so a motion leg carried a
       different head and weight from the route it belongs to (Carlos, Sep 3
       2026: "the arrow head and weight of line is different on motion when it
       should be part of the same group so they all match"). The route keeps ONE
       head, at its end, in its own weight; the motion is a treatment ON that
       line, not a second line with opinions. */
    .pd-field .motion-leg {
        fill: none;
        stroke: var(--pd-motion);
        stroke-width: 2.5;
        stroke-dasharray: 6 5;
        stroke-linecap: round;
        stroke-linejoin: round;
        pointer-events: none;   /* the tap belongs to the route beneath it */
    }
    .pd-field .route[data-weight="s"] ~ .motion-leg { stroke-width: 1.6; }
    .pd-field .route[data-weight="l"] ~ .motion-leg { stroke-width: 3.6; }
    .pd-field .route.stem { marker-end: none; }
    .pd-field .route.opt { stroke-dasharray: 5 4; }                 /* the option: two dashed choices off one stem */
    .pd-field .tok.ghost { stroke-dasharray: 3 3; opacity: 0.55; }  /* where the motion lands him */
    .pd-field .tok-label.ghost { opacity: 0.55; }
    .pd-field .key circle { fill: var(--pd-paper); stroke: var(--pd-defense); stroke-width: 2; }
    .pd-field .key text { font-family: var(--font-ui); font-weight: 800; font-size: 11px; fill: var(--pd-defense); text-anchor: middle; dominant-baseline: central; }
    .pd-field .note { font-family: var(--font-ui); font-weight: 700; font-size: 11px; fill: var(--ink-muted); }
    .pd-field .route.ballpath { stroke: var(--pd-ball); marker-end: url(#pdArrowBall); }
        .pd-field .read { fill: var(--ink); }
    .pd-field .read-n { font-family: var(--font-data); font-size: 9px; fill: var(--pd-on-ink); text-anchor: middle; dominant-baseline: central; }
    .pd-field .sel { fill: none; stroke: var(--pd-select); stroke-width: 3; }
    .pd-field .sel-halo { fill: var(--volt-glow); stroke: none; }
    .pd-field .tree { fill: none; stroke: var(--pd-tree); stroke-width: 1.4; stroke-dasharray: 4 4; }
    .pd-field .tree-n { cursor: pointer; }
    .pd-field .tree-n circle { fill: var(--surface); stroke: var(--border-strong); stroke-width: 1.2; }
    .pd-field .tree-n text { font-family: var(--font-data); font-size: 9px; fill: var(--ink-muted); text-anchor: middle; dominant-baseline: central; }
    .pd-field .tree-n:hover circle { fill: var(--ink); stroke: var(--ink); }
    .pd-field .tree-n:hover text { fill: var(--pd-on-ink); }
    .pd-field .def { fill: none; stroke: var(--pd-defense); stroke-width: 1.8; opacity: 0.3; }
    .pd-field .def-shell { display: none; }
    .pd-field.look .def-shell { display: block; }
    .pd-field:not(.has-sel) .sel, .pd-field:not(.has-sel) .sel-halo { display: none; }
    .pd-field .tree-layer { display: none; }
    .pd-field.tree-open .tree-layer { display: block; }
    .pd-run { display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 0 14px 0 12px; border-radius: var(--r-xs); background: var(--ink); color: var(--pd-on-ink); font-size: 12px; font-weight: 800; letter-spacing: 1.1px; text-transform: uppercase; box-shadow: inset 0 -2px 0 var(--volt); border: 1px solid transparent; }
    .pd-run svg { width: 15px; height: 15px; }
    .pd-run .ico-pause { display: none; }
    .pd-run.live .ico-run { display: none; }
    .pd-run.live .ico-pause { display: inline-flex; }
    /* coach/install.html's button still stops (no scrub bar there) */
    .pd-run .ico-reset { display: none; }
    .pd-run.live .ico-reset { display: inline-flex; }
    .pd-run.live { background: var(--surface); color: var(--ink); border-color: var(--border-strong); box-shadow: none; }
    /* Task 5, the rest of the lift (mock:164-166, verbatim): the Sheet button
       carries BOTH glyphs and shows the one for the face it will take you to —
       the same one-control-two-states grammar `.pd-run.live` above uses. These
       three lines sat unlifted only because the button itself was hidden until
       Task 5 wired it; without them the button paints two icons at once. */
    #pdSheetBtn .ico-sheet, #pdSheetBtn .ico-field { display: inline-flex; }
    #pdSheetBtn .ico-field, #pdSheetBtn.open .ico-sheet { display: none; }
    #pdSheetBtn.open .ico-field { display: inline-flex; }
    .pd-field .key { cursor: grab; }
    .pd-field .key.drag { cursor: grabbing; }
    .pd-field .key.drag circle { fill: var(--pd-defense); stroke-width: 3; }
    .pd-field .key.drag text { fill: var(--pd-paper); }
    .pd-field.running .sel, .pd-field.running .sel-halo, .pd-field.running .tree-layer, .pd-field.running .def-shell { display: none; }
    .pd-field .sim .dfd { fill: var(--pd-paper); stroke: var(--pd-defense); stroke-width: 2; }
    .pd-field .sim .pressure { stroke: var(--pd-defense); stroke-width: 1.5; stroke-dasharray: 3 3; }
    .pd-field .sim .window { fill: var(--volt-glow); stroke: var(--volt); stroke-width: 1.5; }
    .pd-field .sim .glow { fill: none; stroke: var(--volt); stroke-width: 9; stroke-linecap: round; stroke-linejoin: round; opacity: 0.55; }
    .pd-field .sim .flight { fill: none; stroke: var(--pd-ball); stroke-width: 1.5; stroke-dasharray: 2 4; }
    .pd-field .rgroup.leaning .opt:not(.lean) { opacity: 0.4; }   /* where the Mike stands says which way he will break — live, before the snap and during the stem */
    .pd-field .route.opt.on { stroke-dasharray: none; }           /* the branch he took */
    .pd-field .route.opt.off { opacity: 0.2; }                    /* the one he did not */
    .pd-field .tok.caught { stroke: var(--volt); stroke-width: 3.5; }
    .pd-outcome { display: none; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: var(--ink); white-space: nowrap; }
    .pd-canvas.running .pd-outcome { display: inline-flex; }
    .pd-canvas.running .pd-contract { display: none; }
    .pd-menu { position: absolute; left: 18%; top: 6%; width: 172px; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--r-sm); box-shadow: var(--pd-shadow-lift); padding: 6px; z-index: 3; }
    .pd-menu.right { left: auto; right: 12px; top: 46%; }   /* opens on the EMPTY side of the selected player — never over his own routes or tree */
    .pd-menu button { display: flex; align-items: center; gap: 9px; width: 100%; min-height: 36px; padding: 0 10px; border-radius: var(--r-xs); font-size: 14px; font-weight: 700; color: var(--ink); text-align: left; }
    .pd-menu button:hover { background: var(--hover); }
    .pd-menu button svg { width: 15px; height: 15px; color: var(--ink-muted); }
    .pd-menu button.danger { color: var(--live); }
    .pd-menu button.danger svg { color: var(--live); }
    .pd-menu-caption { font-size: 10px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--ink-faint); padding: 6px 10px 4px; }
    .pd-canvas-foot { border-top: 1px solid var(--border); }
    .pd-look { display: flex; align-items: center; gap: 8px; flex: none; }
    /* the hint sits ON the stage (bottom-left, under the rail), so the foot
       holds controls only and never overflows (mock:127-128). */
    .pd-contract { position: absolute; left: 90px; bottom: 12px; font-size: 12px; font-weight: 600; color: var(--ink-muted); pointer-events: none; }
    .pd-contract b { color: var(--ink); font-weight: 800; }
    .pd-sheet { display: none; flex: 1; min-height: 0; overflow: auto; padding: 16px 18px 22px; background: var(--canvas); }
    .pd-canvas.sheet .pd-sheet { display: block; }
    .pd-canvas.sheet .pd-canvas-head, .pd-canvas.sheet .pd-stage, .pd-canvas.sheet .pd-canvas-foot { display: none; }
    .pd-sheet-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; max-width: 860px; margin: 0 auto 10px; }
    .pd-paper { max-width: 860px; margin: 0 auto; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm); box-shadow: var(--pd-shadow); padding: 24px 28px 26px; display: flex; flex-direction: column; gap: 18px; }
    .pd-sheet-head { display: grid; grid-template-columns: 1fr 250px; gap: 22px; align-items: start; padding-bottom: 16px; border-bottom: 2px solid var(--ink); }
    .pd-sheet-title { font-family: var(--font-display); font-weight: 800; font-size: 40px; line-height: 0.95; text-transform: uppercase; letter-spacing: 0.4px; }
    .pd-sheet-sub { font-size: 12px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--ink-faint); margin-top: 6px; }
    .pd-sheet-phil { margin-top: 12px; font-size: 15px; font-weight: 600; line-height: 1.45; color: var(--ink); }
    .pd-sheet-facts { display: grid; grid-template-columns: 92px 1fr; border: 1px solid var(--border-strong); border-radius: var(--r-xs); overflow: hidden; }
    .pd-sheet-facts > span { padding: 8px 10px; font-size: 13px; font-weight: 700; border-bottom: 1px solid var(--border); min-height: 36px; display: flex; align-items: center; }
    .pd-sheet-facts > span:nth-child(odd) { font-size: 10px; letter-spacing: 1.2px; text-transform: uppercase; color: var(--ink-faint); background: var(--surface-elevated); border-right: 1px solid var(--border); }
    .pd-sheet-facts > span:nth-last-child(-n+2) { border-bottom: 0; }
    .pd-sheet-diagram svg { display: block; width: 100%; max-height: 420px; aspect-ratio: 400 / 302; margin: 0 auto; }
    .pd-sheet-diagram svg[data-window="board"] { aspect-ratio: 3 / 2; width: min(100%, 630px); }   /* the sheet is the board, smaller: 3:2 up to the 420px cap, never letterboxed inside a wider box (lane DSG-TILES) */   /* the paper's full width: the diagram is what the sheet is for */
    .pd-sheet-table { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: 13px; }
    .pd-sheet-table th { text-align: left; font-size: 10px; letter-spacing: 1.2px; text-transform: uppercase; color: var(--pd-on-ink); background: var(--ink); padding: 8px 10px; font-weight: 800; }
    .pd-sheet-table td { padding: 9px 10px; border-bottom: 1px solid var(--border); vertical-align: top; font-weight: 600; line-height: 1.4; color: var(--ink); }
    .pd-sheet-table td.pos { font-weight: 800; font-size: 14px; }
    .pd-sheet-table td.route { font-weight: 800; }
    .pd-sheet-table td.num { font-family: var(--font-data); font-size: 12px; }
    .pd-sheet-table td.num small { font-family: var(--font-ui); font-size: 10px; font-weight: 700; color: var(--ink-faint); margin-left: 3px; letter-spacing: 1px; text-transform: uppercase; }
    .pd-sheet-table td.muted { color: var(--ink-faint); }
    .pd-sheet-table .cov { display: grid; grid-template-columns: 64px 1fr; gap: 2px 8px; }
    .pd-sheet-table .cov b { font-weight: 800; color: var(--ink-muted); }
    @media print { body * { visibility: hidden; } .pd-paper, .pd-paper * { visibility: visible; } .pd-paper { position: absolute; left: 0; top: 0; width: 100%; max-width: none; border: 0; box-shadow: none; } }
    /* ── INSPECTOR: what the selection needs (mock:219-291) ──────────────── */
    .pd-inspector { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-card); min-height: 0; overflow: hidden; }
    .pd-insp-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 14px; min-height: 52px; border-bottom: 1px solid var(--border); }
    .pd-insp-title { font-family: var(--font-display); font-size: 17px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .pd-insp-sub { font-size: 12px; font-weight: 700; color: var(--ink-muted); margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .pd-insp-body { flex: 1; min-height: 0; overflow: auto; padding: 14px; display: flex; flex-direction: column; gap: 18px; }
    .pd-insp-state { display: none; flex-direction: column; gap: 14px; }
    .pd-inspector[data-state="token"] .pd-insp-state.token,
    .pd-inspector[data-state="route"] .pd-insp-state.route,
    .pd-inspector[data-state="ball"]  .pd-insp-state.ball,
    .pd-inspector[data-state="play"]  .pd-insp-state.play { display: flex; }
    /* Two label tiers, never one (platform-css.md §5): a SECTION TITLE is the
       card tier — Saira Condensed, full ink, a hairline above it; a FIELD
       LABEL stays 11px, muted, letter-spaced. */
    .pd-sec { display: flex; flex-direction: column; gap: 10px; }
    /* ⛔ `hidden` DID NOT HIDE A SECTION (found Sep 22 2026 on the standalone
       page's first screenshot). The UA sheet's `[hidden] { display: none }` is
       (0,0,0) + an attribute at the lowest origin; `.pd-sec { display: flex }`
       above outranks it, so every `sec.hidden = true` in js/install-design.js
       was a no-op and the section stayed on screen. It showed as the Concept
       panel — "Prints on the sheet" over three empty rows — on any play whose
       concept carries no philosophy, and on every bare play the new designer
       page opens. Closing the CLASS, not the instance: this is one rule for
       every `.pd-sec` the module toggles (#pdPlayPhilSec, #pdRtDepthSec,
       #pdBrSec, #pdQReadSec, #pdCardPreviewSec, #pdPlDelaySec …), and it needs
       no `!important` because (0,2,0) beats (0,1,0). */
    .pd-sec[hidden] { display: none; }
    /* ⛔ THE SAME SPECIES, ONE ROW UP (M4). `.seg { display: inline-flex }`
       (:120) outranks the UA sheet's `[hidden] { display: none }` exactly as
       `.pd-sec` did, so a segmented control hidden in JS stayed on screen —
       found in M4's own screenshot, where the Basic|Pro switch was visible on
       a build that does not offer Pro. One rule for every `.seg` any module
       toggles, for the same (0,2,0)-beats-(0,1,0) reason. */
    .seg[hidden] { display: none; }
    .pd-sec + .pd-sec { border-top: 1px solid var(--border); padding-top: 14px; }
    .pd-sec-head { display: flex; align-items: center; justify-content: space-between; min-height: 24px; }
    .pd-sec-t { font-family: var(--font-display); font-size: 15px; font-weight: 700; letter-spacing: 0.6px; text-transform: uppercase; color: var(--ink); line-height: 1; }
    /* practice.css paints .pr-label --ink-faint (#97a0ad on white = 2.6:1,
       fails AA 4.5:1); muted is 6.4:1 day / 6.5:1 night. Scoped to this rail
       — the shell fix itself belongs in practice.css. */
    #viewDesign .pr-label, .pd-field-block label { font-size: 11px; letter-spacing: 1.2px; color: var(--ink-muted); }
    .pd-sec .pr-input, .pd-sec select.pr-input { min-height: 40px; padding: 8px 12px; font-size: 14px; }
    .pd-sec textarea.pr-input { min-height: 72px; resize: vertical; }
    .pd-sec .seg { align-self: flex-start; }
    .pd-sec .seg-tab { min-height: 34px; }
    .pd-sec .seg.wide { display: flex; } .pd-sec .seg.wide .seg-tab { flex: 1; }
    .pd-quiet { font-size: 11px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--ink-muted); display: inline-flex; align-items: center; gap: 5px; min-height: 28px; padding: 0 8px; border-radius: 6px; }
    .pd-quiet:hover { background: var(--hover); color: var(--ink); }
    .pd-quiet svg { width: 13px; height: 13px; }
    .pd-fields { display: grid; gap: 8px; }
    .pd-field-block label { display: block; font-weight: 700; text-transform: uppercase; margin-bottom: 4px; }
    .pd-two { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
    .pd-stack { display: grid; gap: 10px; margin-top: 8px; }
    .pd-field-block .pd-says { margin-top: 6px; }
    /* the chain: fixed columns, one phrase per cell */
    .pd-chain { display: grid; gap: 4px; }
    .pd-row { display: grid; grid-template-columns: 22px 1fr 52px; align-items: center; gap: 8px; min-height: 36px; padding: 0 10px; border: 1px solid var(--border); border-radius: var(--r-xs); background: var(--surface); font-size: 13px; color: var(--ink); text-align: left; }
    .pd-row:hover { background: var(--hover); }
    .pd-row.on { border-color: var(--ink); box-shadow: inset 3px 0 0 var(--seg-accent, var(--volt)); }
    .pd-row-n { font-family: var(--font-data); font-size: 12px; color: var(--ink-muted); }
    .pd-row-hold { font-family: var(--font-data); font-size: 12px; color: var(--ink-muted); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .pd-empty { font-size: 12.5px; color: var(--ink-muted); padding: 8px 0; }
    .pd-stepper { display: inline-flex; align-items: center; gap: 2px; border: 1px solid var(--border); border-radius: var(--r-xs); background: var(--surface); }
    .pd-stepper .pd-icon { width: 34px; height: 34px; }
    .pd-stepper output { font-family: var(--font-data); font-size: 13px; color: var(--ink); min-width: 52px; text-align: center; }
    .pd-says { font-size: 12.5px; color: var(--ink-muted); line-height: 1.35; }
    /* players on the board: the same tokens, tap to add one that is missing */
    .pd-toks { display: flex; gap: 6px; }
    /* TASK-1 FIX 1: a DEFAULT fill. The six `[data-pos]` rules below only
       cover Q/C/X/Y/Z/H; every other player (QB on 45 of 47 fixture plays,
       RB, and every defender) fell through to no background at all and
       painted white-on-white in Day. Muted ink carries --tok-ink at 5.4:1. */
    .pd-tok { width: 40px; height: 40px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-ui); font-weight: 800; font-size: 13px; color: var(--tok-ink); background: var(--ink-muted); border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--border); }
    .pd-tok.ghost { background: transparent; border: 2px dashed var(--border-strong); box-shadow: none; color: var(--ink-faint); }
    .pd-tok[data-pos="Q"] { background: var(--tok-Q); } .pd-tok[data-pos="C"] { background: var(--tok-C); }
    .pd-tok[data-pos="X"] { background: var(--tok-X); } .pd-tok[data-pos="Y"] { background: var(--tok-Y); }
    .pd-tok[data-pos="Z"] { background: var(--tok-Z); } .pd-tok[data-pos="H"] { background: var(--tok-H); }
    /* the player state's header: the token, the name, the position */
    .pd-player { display: flex; align-items: center; gap: 12px; }
    .pd-player .pd-tok { width: 44px; height: 44px; font-size: 15px; flex: none; }
    .pd-player-name { font-family: var(--font-display); font-size: 20px; font-weight: 800; text-transform: uppercase; line-height: 1; }
    .pd-player-sub { font-size: 13px; font-weight: 600; color: var(--ink-muted); margin-top: 3px; }
    /* the route tree: nine routes from his feet, in the inspector AND on the board */
    .pd-tree { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
    .pd-tree-n { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 8px 4px 6px; border: 1px solid var(--border); border-radius: var(--r-xs); background: var(--surface-elevated); color: var(--ink-muted); min-height: 66px; }
    .pd-tree-n:hover { color: var(--ink); border-color: var(--border-strong); }
    .pd-tree-n.active { background: var(--ink); border-color: var(--ink); color: var(--pd-on-ink); box-shadow: inset 0 -2px 0 var(--volt); }
    .pd-tree-n svg { width: 30px; height: 30px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
    .pd-tree-n b { font-size: 10px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; }
    .pd-tree-n b i { font-style: normal; font-family: var(--font-data); font-size: 8px; opacity: 0.6; margin-right: 3px; }
    .pd-actions-row { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px; }
    .pd-actions-row .pr-btn { min-height: 38px; padding: 0 8px; font-size: 11px; gap: 5px; }
    .pd-actions-row .pr-btn.danger:hover { color: var(--live); border-color: var(--live); }
    .pd-route-glyph { width: 28px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
    /* the inspector foot: the one primary verb of the state, if it has one */
    .pd-insp-foot { padding: 12px 14px; border-top: 1px solid var(--border); display: flex; gap: 8px; }
    .pd-insp-foot .pr-btn { flex: 1; min-height: 40px; }
    .pd-sec-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
    .pd-value { font-size: 14px; font-weight: 700; color: var(--ink); }
    .pd-qread { font-size: 14px; font-weight: 600; color: var(--ink); line-height: 1.45; margin-top: 6px; }   /* the read is a paragraph: left-aligned under its label, never squeezed into a value column */
    .pd-num { font-family: var(--font-data); font-size: 18px; font-weight: 700; color: var(--ink); letter-spacing: 0.5px; }
    .pd-num small { font-family: var(--font-ui); font-size: 11px; font-weight: 700; color: var(--ink-faint); margin-left: 4px; letter-spacing: 1px; text-transform: uppercase; }
    .pd-assign { width: 100%; min-height: 64px; padding: 9px 10px; border: 1px solid var(--border); border-radius: var(--r-xs); background: var(--surface-elevated); font: inherit; font-size: 14px; font-weight: 600; color: var(--ink); line-height: 1.4; resize: none; }
    .pd-assign:focus { outline: none; border-color: var(--border-strong); }
    .pd-chiprow { display: flex; gap: 5px; flex-wrap: wrap; }
    .pd-switch { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: var(--ink-muted); }
    .pd-switch i { width: 34px; height: 20px; border-radius: 999px; background: var(--ink); position: relative; flex: none; }
    .pd-switch i::after { content: ""; position: absolute; top: 3px; right: 3px; width: 14px; height: 14px; border-radius: 50%; background: var(--volt); }
    .pd-field-row { display: grid; grid-template-columns: 76px 1fr; align-items: center; gap: 8px; padding: 6px 0; }
    .pd-preview { border: 1px solid var(--border); border-radius: var(--r-xs); overflow: hidden; background: var(--pd-paper); }
    .pd-preview svg { display: block; width: 100%; aspect-ratio: 400 / 302; }
    .pd-preview svg[data-window="board"] { width: 100%; aspect-ratio: 3 / 2; }   /* width restated: a board svg rule carries its own definite size (audit-svg-sizing) */   /* SC_BOARD_GEOMETRY.aspect — the card is the board, smaller (lane DSG-PREVIEW, Oct 11 2026) */
    .pd-keys { padding: 10px 16px; border-top: 1px solid var(--border); font-size: 11px; font-weight: 700; color: var(--ink-faint); letter-spacing: 0.4px; display: flex; gap: 10px; flex-wrap: wrap; }
    .pd-keys kbd { font-family: var(--font-ui); font-weight: 800; color: var(--ink-muted); border: 1px solid var(--border-strong); border-radius: 4px; padding: 0 5px; }
    .pd-vars { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-card); overflow-x: auto; }
    .pd-vars-head { display: flex; flex-direction: column; gap: 2px; padding-right: 8px; margin-right: 2px; border-right: 1px solid var(--border); flex: none; }
    .pd-vars-head .pd-num { font-size: 16px; }
    .pd-var { display: flex; align-items: center; gap: 9px; flex: none; width: 236px; padding: 5px 9px 5px 5px; border: 1px solid var(--border); border-radius: var(--r-xs); background: var(--surface); color: var(--ink-muted); text-align: left; }
    .pd-var:hover { background: var(--hover); color: var(--ink); }
    .pd-var.active { border-color: var(--ink); box-shadow: inset 0 -3px 0 var(--volt); color: var(--ink); }
    .pd-var svg { display: block; width: 88px; flex: none; aspect-ratio: 400 / 302; border-radius: 4px; background: var(--pd-paper); }
    .pd-var svg[data-window="board"] { width: 88px; aspect-ratio: 3 / 2; }   /* a tile is the board, smaller (lane DSG-TILES); width restated: a board svg rule carries its own definite size (audit-svg-sizing) */
    .pd-var-words { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
    .pd-var-week { font-size: 10px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--ink-faint); padding: 0 3px; }
    .pd-var-label { font-size: 12px; font-weight: 700; line-height: 1.15; padding: 0 3px 2px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    .pd-var-add { justify-content: center; align-items: center; width: auto; min-height: 76px; padding: 0 18px; border-style: dashed; border-color: var(--border-strong); color: var(--ink-muted); font-size: 13px; font-weight: 700; gap: 6px; }
    .pd-var-add svg { width: 16px; height: 16px; aspect-ratio: auto; background: none; }
/* ── ADDITIONS beyond the lift (Lane 2a, ≤4 permitted by the brief; 2 used —
   the ball marker and stroke needed no addition: `.pd-field .route.ballpath`
   (mock:139, lifted verbatim above at :110) already carries them; see
   lane2a-fix1-report.md) ──────────────────────────────────────────────── */

/* ADDITION 1 of 2: a route this surface marks `.dashed` (IDD.polyline's
   segment.dashed/route-dashed fold, OR route.isOption === true per
   js/play-renderer.js:659-663) — the mock has `.route.opt` for the specific
   case of an option-branch path, but no general-purpose dashed rule. */
.pd-field .route.dashed { stroke-dasharray: 5 4; }

/* ADDITION 2 of 2: `endType === 'none'` — draw the route with NO end marker.
   The mock defines the other five endings (arrow default + chevron, block,
   dot, hitch, curl) but never a "no marker" case. */
.pd-field .route[data-end="none"] { marker-end: none; }

/* ── ADDITIONS beyond the lift (Lane 2b, ≤6 permitted by the brief, each
   prefixed `.pd-var `; 3 used) — the variations strip (js/install-design.js)
   reuses js/install-design-svg.js's mount()/paintOption() UNCHANGED on an
   88px-wide still, so its strokes/tokens/LOS are the same absolute values
   the big field paints (route stroke 2.5, tok r=11, LOS stroke 2.5). The
   mock's own stills (mock:704-729, NOT lifted — a toy <symbol> demo, not
   this surface's real paint path) used stroke 6 / r=14 / LOS stroke 4 at
   the SAME 400-wide viewBox specifically for legibility at that small size.
   These three rules restate the mock's own still values on the real
   surface, listed here per the brief. ────────────────────────────────── */
.pd-var .pd-field .route { stroke-width: 6; }
.pd-var .pd-field .tok { r: 14; }
.pd-var .pd-field .los { stroke-width: 4; }

/* ADDITION (lane 2b fix 1): touch rules — default touch-action:auto lets
   iPadOS read a finger drag on #pdField as a page pan (pointercancel fires
   mid-gesture and a partial move commits instead of the real one), and a
   500ms hold on a token raises the iOS text-selection callout on the SVG
   <text> labels before the long-press menu opens. */
.pd-field { touch-action: none; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.pd-field text { pointer-events: none; }

/* ADDITION (lane 2c): the draft stroke — the freehand path a coach is
   currently drawing, before it becomes a route (js/install-design-svg.js's
   draftPath). Everything above this line is byte-for-byte the lift plus
   the prior lanes' marked additions. */
.pd-field .route.draft { opacity: .6; }

/* ADDITION (lane 2c fix 1): the tree node's hit target sits UNDER the
   painted circle (r=9, ≈33px — too small for the lane's 44px law); r=13
   (≈48px) on a transparent, stroke-none circle, with pointer-events:all so
   a tap anywhere in the larger ring reaches it. Specificity (0,3,0) beats
   `.pd-field .tree-n circle` (0,2,1) so the painted circle keeps its own
   fill/stroke unaffected by this rule. */
.pd-field .tree-n .tree-hit { fill: transparent; stroke: none; pointer-events: all; }

/* ADDITION (lane 3): the run — the engine's ticks on the surface. Everything
   above this line is byte-for-byte the lift plus the prior lanes' marked
   additions. */
.pd-field .def-spot { cursor: grab; }
.pd-field .def-spot.drag { cursor: grabbing; }
.pd-field .def-spot .def-hit { fill: transparent; stroke: none; pointer-events: all; }
.pd-field .def-spot.drag .def { opacity: 1; }
.pd-field .def.rush { stroke-dasharray: 3 3; }
.pd-field .route.open { stroke: var(--volt); stroke-width: 4; }
.pd-field.running .def-spot { display: none; }
.pd-look .fr-coverage-select { min-width: 160px; }

/* ── ADDITIONS (Task 1, the v2 chrome) ───────────────────────────────────
   Everything above this line is the Lane 2a/2b/2c/3 lift plus the v2 chrome
   lift documented in the header. Three rules the mock's markup implies but
   its <style> never spells out. */

/* 1. The token-size control (mock:350 tokMinus/tokPlus -> #pdTokMinus /
   #pdTokPlus). The brief's id map: "writes --pd-tok-r on #pdField (paint
   only, never saved)" — which needs exactly one rule to read it. The
   fallback is the radius js/install-design-svg.js already paints (:351,
   r=11), so an untouched control changes nothing, and the variations-strip
   still (`.pd-var .pd-field .tok`, three classes) still outranks it. */
.pd-field .tok { r: var(--pd-tok-r, 11px); }

/* 2. The token-size group in the board head: a label and two 40px steppers
   that must not stretch (mock:350's token-size group). */
.pd-toksize { gap: 2px; }
.pd-toksize .pr-label { margin-right: 6px; }

/* 3. The board head holds three groups on one 52px row; on a 1024-wide iPad
   the middle group is the one that may wrap its label away first. */
.pd-canvas-head { flex-wrap: wrap; row-gap: 6px; }
/* ⛔ WRAPPING THE HEAD WAS HALF THE FIX (Sep 21 2026). Measured in WebKit at an
   iPad's 1180x820: the head wraps as intended and its second group lands on a
   row of its own — but THAT GROUP IS ONE UNBREAKABLE LINE of 578px inside a
   524px card, so it ran 66px past the card's right edge and `.pd-canvas`'s
   `overflow: hidden` silently cut the end off it. The end of that group is the
   Full/Half speed segment, which is why the control a coach could not find was
   the playback speed. `flex-wrap` on a container only moves whole children;
   a child wider than the container still overflows. The board head's own
   groups wrap too now, so the row breaks INSIDE the group instead of running
   off the card. `> .pd-group` is deliberate: `.pd-toksize` is a group nested
   inside one of these, and its label and its two steppers travel together. At
   1366x1024 the head measures 710px, the group fits, and nothing wraps — this
   rule costs that layout nothing. */
.pd-canvas-head > .pd-group { flex-wrap: wrap; row-gap: 6px; }

/* 4 (TASK-1 FIX 6): a segmented tab the open play has no source for. The
   mock's `.seg` has no disabled state; `#pdLabelSeg`'s Name and # tabs are
   disabled whenever no player on the board carries a name / a number, so the
   control says what it can do instead of doing nothing when pressed. */
.seg-tab:disabled { opacity: 0.45; pointer-events: none; }

/* ── ADDITIONS (Task 2, the football's chain) ─────────────────────────────
   Everything above this line is the earlier lanes' lift plus their marked
   additions. Five classes the v2 mock defines for exactly these shapes
   (design-directions/play-designer-v2.html:166-167, 184-188, 193-195), with
   the mock's raw palette swapped for this rail's tokens — the surface may
   never paint a class the stylesheet has no rule for. */

/* 1. The football at each transfer, and the wide invisible twin a finger
   actually grabs. The glyph itself is ~10px across; the twin is r=16 USER
   units, which at the 1024 board's ~1.38 CSS px per unit is ≈44 CSS px —
   the lane's law (fix 1, I2: it was r=11, ≈30 CSS px, and this comment
   claimed 44). The glyph never takes the pointer, so the tap lands on the
   twin; tokens paint last, so a token always wins the tie. */
.pd-field .chain .football { fill: var(--pd-ball); stroke: var(--tok-ink); stroke-width: 1; pointer-events: none; }
.pd-field .chain .hitball { fill: transparent; stroke: none; cursor: grab; }
/* the center's football's tap target, under the discs (js/install-design-svg.js buildTokens) */
.pd-field .tokens .snap-hit { fill: transparent; stroke: none; cursor: pointer; }
/* 1b. Production's chain (js/playmaker.js:2150-2200, :2955-2974 — the
   drag-to-hand-off port, Sep 24 2026): each HANDOFF is a football on a faint
   ring where the ball changes hands; the LIVE football (where the ball ends
   up) wears a "+" — drag from it to extend the chain. While a finger carries
   the ball, a dashed line runs from where it rests to the finger and a ring
   marks where it will land: ball colour = a pass, select colour = a handoff. */
.pd-field .chain .handoff-ring { fill: var(--surface); fill-opacity: 0.55; stroke: var(--pd-ball); stroke-width: 1; pointer-events: none; }
/* ONE BALL (Sep 24 2026): while the play runs, the resting chain steps aside —
   the only football on the board is the one in play */
.pd-field.running .pd-play .chain { display: none; }
.pd-field .chain .ball-plus { fill: var(--pd-ball); font: 800 9px var(--font-ui); pointer-events: none; }
.pd-field .balldrag { pointer-events: none; }
.pd-field .balldrag-line { fill: none; stroke: var(--pd-ball); stroke-width: 1.6; stroke-dasharray: 5 4; }
.pd-field .balldrag-ball { fill: var(--pd-ball); stroke: var(--tok-ink); stroke-width: 1; }
.pd-field .balldrag-land { fill: none; stroke: var(--pd-ball); stroke-width: 1.6; }
.pd-field .balldrag-land[data-kind="handoff"] { stroke: var(--pd-select); stroke-dasharray: 3 2; }
/* the forward-pass rule (Sep 24 2026): an illegal throw is red — its target
   ring while dragging, and on the board a ring + PAST LOS where it would leave */
.pd-field .balldrag-land[data-kind="illegal"] { stroke: var(--live); stroke-width: 2.2; }
/* the defence's zone rings and man tethers (Sep 24 2026) — production's
   SCCoverageSim.paint, in SVG: deep --info, hook --gold, flat --turf; the
   opacity of each ring is set per isoline by the painter */
.pd-field .zone-fields { pointer-events: none; }
.pd-field .zone-iso { fill: none; stroke-width: 1; }
.pd-field .zone-iso[data-level="deep"] { stroke: var(--info); }
.pd-field .zone-iso[data-level="hook"] { stroke: var(--gold); }
.pd-field .zone-iso[data-level="flat"] { stroke: var(--turf); }
.pd-field .zone-word { font: 700 5px var(--font-ui); letter-spacing: 0.5px; fill-opacity: 0.9; }
.pd-field .zone-word[data-level="deep"] { fill: var(--info); }
.pd-field .zone-word[data-level="hook"] { fill: var(--gold); }
.pd-field .zone-word[data-level="flat"] { fill: var(--turf); }
.pd-field .man-tether { stroke: var(--live); stroke-opacity: 0.9; stroke-width: 0.6; stroke-dasharray: 1 1.5; pointer-events: none; }
/* the flag pull (real physics, Sep 24 2026): a red burst where the flag came off */
.pd-field .flag-pull { pointer-events: none; }
.pd-field .flag-pull circle { fill: var(--live); fill-opacity: 0.25; stroke: var(--live); stroke-width: 2; }
.pd-field .flag-pull text { fill: var(--live); font: 800 8px var(--font-ui); letter-spacing: 1px; }
/* spacing warnings (Sep 24 2026): gold is advice, red is a rule */
.pd-field .warnings { pointer-events: none; }
.pd-field .warn-ring { fill: none; stroke: var(--gold); stroke-width: 1.4; stroke-dasharray: 4 3; }
.pd-field .warn-tag { fill: var(--gold); font: 800 7px var(--font-ui); letter-spacing: 0.8px; }
.pd-field.running .warnings { display: none; }
/* the yard readout (Sep 24 2026): ink plate, chrome type, numbers a coach
   reads at a glance while his finger is still down */
.pd-field .readout { pointer-events: none; }
.pd-field .readout rect { fill: var(--ink); fill-opacity: 0.92; }
.pd-field .readout text { fill: var(--pd-on-ink); font: 800 9px var(--font-ui); letter-spacing: 0.4px; }
.pd-field .chain .rule-flag { fill: none; stroke: var(--live); stroke-width: 1.6; stroke-dasharray: 3 2; pointer-events: none; }
.pd-field .chain .rule-tag { fill: var(--live); font: 800 7px var(--font-ui); letter-spacing: 0.8px; pointer-events: none; }
.pd-row.rule { box-shadow: inset 3px 0 0 var(--live); }
.pd-says.pd-rule { color: var(--live); }

/* 2. The direct snap: a thin dashed line C -> receiver, drawn only when the
   snap is not the quarterback (mock:195 / js/playmaker.js:2017-2021). */
.pd-field .chain .snapline { fill: none; stroke: var(--pd-ball); stroke-width: 1.2; stroke-dasharray: 2 3; opacity: 0.8; pointer-events: none; }

/* 3. The joints a branch may leave from, ringed only while + Branch is
   armed (mock:184-188). The ring is the mock's r=6; the TAP is the r=16
   twin beside it (fix 1, I2 — the ring alone was a ≈16 CSS px target). */
.pd-field .joints { display: none; }
.pd-field.branch-arm .joints { display: block; }
.pd-field .joint { fill: transparent; stroke: var(--pd-select); stroke-width: 1.6; cursor: pointer; }
/* the joint the option will leave from — production's "filled ring, no ink"
   (js/playmaker.js:5236-5241): picked, waiting for the tap that places the leg. */
.pd-field .joint.armed { fill: var(--pd-select); }
.pd-field .joint-hit { fill: transparent; stroke: none; pointer-events: all; cursor: pointer; }

/* 4. The release badge on a token — the same D1/D2/D3 the engine paints
   (mock:166-167 / js/playmaker.js:4094). It rests where the player rests, so
   it goes away while the run is moving him. */
.pd-field .tok-delay { font-family: var(--font-ui); font-weight: 800; font-size: 7px; fill: var(--pd-on-ink); text-anchor: middle; dominant-baseline: central; pointer-events: none; }
.pd-field .tok-delay-bg { fill: var(--ink); stroke: var(--surface); stroke-width: 1; pointer-events: none; }
.pd-field.running .tok-delay, .pd-field.running .tok-delay-bg { display: none; }

/* 5. A branch row is who/what plus ONE action: the row opens the branch, the
   trash removes it (a row-law list, `.pd-row`'s own three columns kept).
   Fix 1 (I3): the trash keeps `.pd-icon`'s own 40px — a destructive single
   action is not the stepper's compressed pair, and nothing about `.pd-brow`
   comes from a mock, so there was no size to match. */
.pd-brow { display: grid; grid-template-columns: 1fr 40px; gap: 4px; align-items: center; }
.pd-brow .pd-icon { color: var(--live); }

/* 6 (fix 1, I4). `+ Transfer` / `+ Branch` keep the mock's 28px LOOK and get
   a 44px TAP: a transparent 8px skirt on the same element, no layout change
   (the section head is a flex row, the skirt is absolutely positioned). */
.pd-quiet { position: relative; }
.pd-quiet::before { content: ''; position: absolute; inset: -8px; }

/* ── ADDITIONS (Task 3): the route's KIND is its colour, and the caps wear it.
   Everything above this line is the lift plus the prior lanes' marked
   additions.

   js/install-design-svg.js writes `data-kind` (the WORD for route.color:
   pass · run · pa, anything else read as pass) on every `.route` it paints,
   and mounts one marker per cap per kind (`pdEnd-<cap>-<kind>`, the mock's
   own generated set, mock:755-767). These rules are the only place the two
   meet: a route takes its kind's stroke and its kind's cap, and NOTHING here
   carries a colour literal — every value is a `--route-*` token.

   The `:not()` guards keep three earlier rules that colour a route for a
   REASON OTHER than its kind: `.motion` (pre-snap, --pd-motion), `.open`
   (lane 3's window glow, --volt) and `.ballpath` (the football's flight,
   --pd-ball). Without them these later, higher-specificity rules would
   repaint all three. */
.pd-field .route[data-kind="pass"] {
    --pd-kind: var(--route-pass);
    --cap-arrow: url(#pdEnd-arrow-pass); --cap-chevron: url(#pdEnd-chevron-pass);
    --cap-block: url(#pdEnd-block-pass); --cap-dot: url(#pdEnd-dot-pass);
    --cap-hitch: url(#pdEnd-hitch-pass); --cap-curl: url(#pdEnd-curl-pass);
}
.pd-field .route[data-kind="run"] {
    --pd-kind: var(--route-run);
    --cap-arrow: url(#pdEnd-arrow-run); --cap-chevron: url(#pdEnd-chevron-run);
    --cap-block: url(#pdEnd-block-run); --cap-dot: url(#pdEnd-dot-run);
    --cap-hitch: url(#pdEnd-hitch-run); --cap-curl: url(#pdEnd-curl-run);
}
.pd-field .route[data-kind="pa"] {
    --pd-kind: var(--route-pa);
    --cap-arrow: url(#pdEnd-arrow-pa); --cap-chevron: url(#pdEnd-chevron-pa);
    --cap-block: url(#pdEnd-block-pa); --cap-dot: url(#pdEnd-dot-pa);
    --cap-hitch: url(#pdEnd-hitch-pa); --cap-curl: url(#pdEnd-curl-pa);
}
.pd-field .route[data-kind]:not(.motion):not(.open):not(.ballpath) { stroke: var(--pd-kind); marker-end: var(--cap-arrow); }
.pd-field .route[data-kind][data-end="chevron"]:not(.motion):not(.ballpath) { marker-end: var(--cap-chevron); }
.pd-field .route[data-kind][data-end="block"]:not(.motion):not(.ballpath)   { marker-end: var(--cap-block); }
.pd-field .route[data-kind][data-end="dot"]:not(.motion):not(.ballpath)     { marker-end: var(--cap-dot); }
.pd-field .route[data-kind][data-end="hitch"]:not(.motion):not(.ballpath)   { marker-end: var(--cap-hitch); }
.pd-field .route[data-kind][data-end="curl"]:not(.motion):not(.ballpath)    { marker-end: var(--cap-curl); }
.pd-field .route[data-kind][data-end="none"] { marker-end: none; }

/* MOTION is now the squiggle js/playmaker.js:4210-4249 draws (the mock's own
   squiggle(), mock:770, ported into js/install-design-svg.js's routeD). The
   lifted `.route.motion` rule dashed a STRAIGHT line to say "pre-snap"; a
   dashed squiggle reads as neither, so the dash comes off and the wave
   carries the meaning. Colour and marker stay the lift's. */
.pd-field .route.motion { stroke-dasharray: none; }

/* Task 4, ruling I-5: the WRAP override that lived here is deleted. The foot
   carried five groups (Vs look · Kind · Line · End · Weight) where the mock's
   carries four, so it wrapped — into THREE rows at 1024, eating ~130px of the
   board. Vs look and Result moved to the inspector's Concept · look section
   and the foot is the mock's four groups again, on the mock's own
   `space-between` (:178), one row, 59px at every width.
   MEASURED at the coach's own width (fix round 1, I-2a — the first numbers
   here were taken in a bare-body harness and read ~1.5x too wide). The canvas
   column inside `.pr-shell` is 712px at 1366x1024 (the 12.9" iPad Pro in
   landscape), 540px at 1194x834 (the 11"), 432px at 1024x768 and 786px at
   1440x900; the foot's own box is 24px less. Full-dress, the four groups want
   1049px — more than any of them. Folded (below) they want 682px, which fits
   686 at 1366x1024 and 760 at 1440x900, and still exceeds 514 at 1194x834 and
   406 at 1024x768, where the bar scrolls. So below 1049px of foot the bar
   FOLDS, in the mock's own glyph-tab grammar, and only then does it scroll:
     · each group's `pr-label` folds into the seg's `aria-label` (the label is
       a word about the control, and the control keeps saying it to a screen
       reader) — the group's own 8px label gap goes with it;
     · Kind's three tabs fold to their swatch, the word moving to `title`,
       which is what Line/End/Weight have always been: a glyph and a title;
     · the between-group gap falls from 14px to 8px, the ladder's next rung.
   The scroll below it stays as the LAST resort (it is the platform's rule for
   content wider than its container, and what `.pd-vars` under it does), so a
   window narrower than the folded bar still reaches every group instead of
   hiding one. `space-between` survives: with no free space flex packs from
   the start, so nothing is clipped at the left edge.
   `overflow-x` forces `overflow-y` to `auto` too, which clipped the 4px tap
   skirt each seg tab wears below it (the 44px rule below); the 4px padding
   gives the skirt room INSIDE the clip box and the equal negative margin
   hands the height straight back, so the foot is still 59px. */
.pd-foot-groups { overflow-x: auto; scrollbar-width: none; padding: 4px 0; margin: -4px 0; }
.pd-foot-groups::-webkit-scrollbar { display: none; }   /* the bar would eat the 44px tap skirt */

/* THE CONTAINER IS THE FOOT, not the viewport — the column is what runs out
   of room, and at one viewport width the column changes with the rail and the
   inspector beside it. Prior art for container queries in this repo:
   css/playmaker.css:3476-3477 + :3641 and :4578 + :4598. It is declared on
   `.pd-canvas-foot` rather than `.pd-canvas` (the ruling's suggestion) because
   the foot's inline size IS the canvas column's, minus its own 12px padding,
   and `container-type` brings layout containment with it: on `.pd-canvas` that
   would wrap the board's SVG stage, the absolutely-placed token menu and the
   sheet as well, for a query only the foot asks. */
.pd-canvas-foot { container-type: inline-size; container-name: pdfoot; }
@container pdfoot (max-width: 1048px) {
    .pd-foot-groups { gap: 8px; }
    .pd-foot-groups .pd-group > .pr-label { display: none; }
    #pdFootKind .pd-tab-word { display: none; }
    #pdFootKind .pd-swatch { margin-right: 0; }
    #pdFootKind .seg-tab { padding: 0 9px; }   /* the glyph-tab value, :175 */
    /* The glyph tabs go from 46px wide to exactly 44 — the tap law's own
       number, and the last 30px the bar needed to fit 1366x1024 without a
       scroll (682 of 686). `:175`'s `:not(:has(…))` scores (0,3,0), so this
       restates the foot in the selector to match it and win on source order. */
    .pd-canvas-foot .pd-foot-groups .seg-tab:not(:has(.pd-swatch)) { padding: 0 8px; }
}

/* The look select is a 38px control (css/install.css:397) and it now sits
   where a thumb reaches for it. A `<select>` cannot wear the transparent
   skirt Tasks 2-3 gave `.pd-quiet` and the foot tabs — the UA owns its box —
   so in the inspector it is really 44px, the rail's own target. */

/* The Result row moved with the Vs look select (I-5). Its lifted visibility
   rule is `.pd-canvas.running .pd-outcome` (:263) and the inspector is not
   inside `.pd-canvas`, so js/install-design.js's setOutcome() marks the row
   itself — shown only while there is an outcome to show. The hidden half is
   restated here, not borrowed from the lift: the row is also a
   `.pd-field-row`, and THAT rule (:393) sits after `.pd-outcome`'s
   `display:none` (:262) at equal specificity, so the lift alone left an empty
   Result row painted at rest. The id carries both halves.
   ⛔ `.pd-canvas.running .pd-outcome` (:263) is now INERT — it is kept because
   it sits above the fidelity marker, inside the lift, and the lift is copied
   byte-for-byte; nothing in this build has a `.pd-outcome` inside
   `.pd-canvas` any more. Do not read it as the rule that shows the Result. */
#pdInspector .pd-outcome { display: none; }
#pdInspector .pd-outcome.on { display: grid; }

/* Fix round 1 (M-4): the board foot's seg tabs are 36px tall — a faithful lift
   of the mock (mock:120, .seg-tab min-height 36) — against this lane's 44px
   target. Same treatment as Task 2's `.pd-quiet`: the tab LOOKS the mock's
   36px and TAPS 44, via a transparent 4px skirt above and below on the same
   element. `::after` is taken (`.seg-tab.active`'s volt bottom edge, :124), so
   the skirt is `::before`; it is absolutely positioned, so it is out of flow
   and the tab's own flex layout, its glyph and that volt edge are untouched.
   `inset: -4px 0` — vertical only: the tabs sit 2px apart inside one `.seg`
   plate, and a horizontal skirt would overlap the neighbour's. The
   `position: relative` restates :121's (which the volt edge already depends
   on) so this rule is readable on its own. */
.pd-canvas-foot .seg-tab { position: relative; }
.pd-canvas-foot .seg-tab::before { content: ''; position: absolute; inset: -4px 0; }

/* ── FINAL ROUND: the last three controls under the 44px target ───────────
   The lift's own values stay where they are (:156, :242, :275 are inside the
   byte-for-byte block); these are the additions that close them, each in the
   grammar the foot tabs already established above.

   1. THE TOKEN MENU IS NOT A MOCK-PIXEL SURFACE. `.pd-menu button` lifts the
      mock's 36px row — but the mock's menu is a DESKTOP right-click menu, and
      this one is what a long press opens on an iPad, the one door the
      interaction contract names for Route tree / Detach route / Coaching
      point / Flip side / Delete. A 36px row inside a 172px card is five
      neighbours a thumb can hit by mistake, so the row goes to 44 OUTRIGHT
      (no skirt: overlapping skirts inside a 6px-padded stack would put the
      Delete row's hit area over Flip side's).
   2. ⛔ THE SKIRT CANNOT WORK ON `.pd-topbar .pr-btn` OR `.pd-run`, AND THE
      MECHANISM IS WHY, NOT TASTE. Chromium gives a `<button>` a CONTROL CLIP
      that clips its overflow for HIT-TESTING, and the clip is present exactly
      when the button HAS A BORDER. Measured Sep 2 2026 with real
      `elementFromPoint` over seven one-property variants: a bare button, one
      with `background: transparent`, one with `appearance: none`, one with a
      `z-index:1` skirt and one with `overflow: visible` ALL returned the
      parent DIV 2 px above the top edge; only `border: 0` (with or without a
      background) returned the button. That is why the foot's `.seg-tab` skirt
      (:696-697) works — `.seg-tab` declares `border: 0` (:121) — and why the
      same three lines on these two would have painted a hit area that does
      not exist. `border: 0` is not available here: BOTH carry a visible
      border in a state the surface uses (`.pd-run.live` :247 and
      `.pr-btn--ghost` practice.css:220 each set `border-color`).
      So they go to 44 OUTRIGHT — which for `.pr-btn` is not a new value at
      all, it is the platform's own (`practice.css:196` is `min-height: 44px`;
      the lift's :156 was the override that took it DOWN to 40). `.pd-topbar`
      is `min-height: 52px` (:138) with `align-items: center`, so a 44px row
      fits inside the bar it already has. */
.pd-menu button { min-height: 44px; }
.pd-topbar .pr-btn, .pd-run { min-height: 44px; }

/* ── THE PLAY ALONE (sim view off) ────────────────────────────────────────
   The defenders and the glow are simply not painted while the view is off —
   this hides the layer they live in so a frame that slipped through cannot
   flash them, and so the ghosted look under the routes goes with them. The
   run itself is unchanged: the tokens and the ball still move. */
.pd-field.sim-off .sim,
.pd-field.sim-off .dfd,
.pd-field.sim-off .pressure,
.pd-field.sim-off .window { display: none; }
