feat(05-04): route paint stack + endpoint markers + load-hook install + shell chrome

- route-layers.ts: 2 empty GeoJSON sources + 4 line layers (route-alt-casing
  5px matte-black / route-alt-line 3px soft-beige / route-casing 9px
  matte-black / route-line 5px signal-orange), opacity 1.0, cap square,
  join bevel, no beforeId — idempotent install, setData-only updates,
  zero setStyle (T-05-09); verbatim tokens.css hex (DSGN-01)
- route-markers.ts: origin ring (24px, 2px matte-black, aria-hidden) +
  destination pin (12px square + 3px×8px tail, white inset rule, never
  orange) + 160px ellipsis name chip; WeakMap-tracked removal
- map.ts: bootMap gains opts.onLoad — invoked inside the existing load
  handler AFTER building-3d, BEFORE the ready log, its own try/catch
- shell.css: .route-stack registers (64px control-clearance composite),
  telemetry/diff/chips rows composing the .card child rules (no padding
  overrides), 28px chips with 44px ::after hit slop + instant :active
  invert + 3px focus bracket, marker chrome — token references only
This commit is contained in:
araharah-executor
2026-10-24 05:29:25 +08:00
parent d0751c1a33
commit 01a75f1fbe
4 changed files with 294 additions and 2 deletions
+34 -1
View File
@@ -4,7 +4,11 @@
* Every value is a token reference (var(--color-*) / var(--spacing-*) /
* var(--type-*)); the only literals are the machined-border geometry the
* UI-SPEC enumerates (1px/2px rules, 8px lamp/gantry/notch, 40x40 plate,
-2px hit slop, 320px card max-width, 20px dot as SVG r=10). Zero
-2px hit slop, 320px card max-width, 20px dot as SVG r=10; Phase 5 adds:
chip plate 28px tall + -8px hit slop (44px target), origin ring 24px/2px,
destination pin 12px square + 3px×8px tail (20px glyph), route line
widths 9/5/3 map-space paint, the 64px control-clearance composite,
160px name-chip max-width, 3px focus bracket). Zero
* roundedness (the one `border-radius: 0` reset pins MapLibre's chrome),
* zero transition/animation — state changes are instant hard swaps.
*
@@ -102,5 +106,34 @@ main { position: fixed; inset: 0; }
.pos-cone { fill: var(--color-signal-orange); }
.pos-group[data-stale="true"] { opacity: 0.4; }
/* route state stack (second register holder — UI-SPEC §State Coexistence).
The Phase 3 .card-stack logic stays byte-identical; the route surface gets
its OWN stack so the pending card (center) and the signal-lost card
(bottom, Phase 3 stack) can coexist in different registers. */
.route-stack { position: absolute; left: var(--spacing-margin); right: var(--spacing-margin); max-width: 320px; margin-inline: auto; }
.route-stack[data-register="center"] { top: 50%; transform: translateY(-50%); }
.route-stack[data-register="bottom"] { top: auto; transform: none; bottom: calc(var(--safe-bottom, 0px) + 64px); }
/* route card body rows — token rhythm composing with the existing .card child
rules (NOT overrides; UI-SPEC post-approval padding addendum) */
.route-telemetry { display: flex; align-items: baseline; gap: var(--spacing-space-xs); padding-inline: var(--spacing-space-md); padding-top: var(--spacing-space-xs); }
.route-diff { padding-inline: var(--spacing-space-md); padding-top: var(--spacing-space-xs); }
.route-chips { display: flex; gap: var(--spacing-space-xs); padding-inline: var(--spacing-space-md); padding-top: var(--spacing-space-sm); }
.route-chips:last-child { padding-bottom: var(--spacing-space-sm); }
/* chips (UI-SPEC chip contract): 28px plate, 44px hit target, instant invert.
No persistent selected-chip state — the selected route lives in the card. */
.route-chip { position: relative; border: 0; border-radius: 0; padding: var(--spacing-space-xs) var(--spacing-space-sm); background: var(--color-matte-black); color: var(--color-pure-white); font: var(--type-label-sm); letter-spacing: var(--type-label-sm-tracking); text-transform: uppercase; white-space: nowrap; }
.route-chip::after { content: ""; position: absolute; inset: -8px; }
.route-chip:active { background: var(--color-signal-orange); color: var(--color-matte-black); }
.route-chip:focus-visible { outline: 3px solid var(--color-signal-orange); outline-offset: 2px; }
/* endpoint markers (map furniture — SVG chrome, machined geometry) */
.route-origin-ring circle { fill: none; stroke: var(--color-matte-black); stroke-width: 2; }
.route-dest-pin-glyph rect { fill: var(--color-matte-black); }
.route-dest-pin-glyph polygon { fill: var(--color-matte-black); }
.route-dest-pin-glyph .inset-rule { fill: none; stroke: var(--color-pure-white); stroke-width: 1; }
.route-dest-namechip { position: absolute; bottom: calc(100% + var(--spacing-space-sm)); left: 50%; transform: translateX(-50%); max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; background: var(--color-matte-black); color: var(--color-pure-white); font: var(--type-label-sm); letter-spacing: var(--type-label-sm-tracking); text-transform: uppercase; padding: var(--spacing-space-xs) var(--spacing-space-sm); }
/* No CSS transition or animation anywhere — state changes are instant hard swaps. */
/* DEBUG-ROUND probe styles removed at the device-matrix exit (2026-10-23). */