Files
arah2/web/src/shell.css
T
araharah-executor 79836f0a31 feat(06-05): guidance/confirm/trip-end card builders + the token-only shell.css guidance block (GREEN)
- buildGuidanceCard: GUIDE register, verbatim headline, live telemetry row (MIN / {km} KM / ETA), 4px progressbar with full aria value set + instant inline width, no-reroute state line only when degrading, ONE btn-secondary END TRIP
- buildConfirmCard: GUIDE // CONFIRM + exact en/ms privacy copy, btn-primary YES ABANDON + btn-secondary CONTINUE, guide-confirm bottom-register hook — the ONE primary CTA in guidance chrome
- buildTripEndCard: GUIDE // END, exact deletion copy, no actions
- shell.css guidance block: banner chassis + 1px separator, CSS-only strip compaction under [data-guidance], list panel + row states + lamp, progress bar — token vars only, zero motion/depth/radius/hex (theme audit clean)
2026-10-24 14:18:21 +08:00

208 lines
15 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* shell.css — the Rams chrome (UI-SPEC §Shell Layout & Register Bands).
* -----------------------------------------------------------------------
* 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; 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.
*
* D-10 / PX-02: NO MapLibre rule participates in theme slots. The map
* style is Rams-constant; only --shell-bg / --shell-on-surface re-slot
* behind [data-tg-theme].
*/
/* tokens.css wins; mockups lose. Value discipline per theme/src/components.css lines 9-15. */
@import "@araharah/theme/dist/rams.css";
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
background: var(--shell-bg);
color: var(--shell-on-surface);
font: var(--type-body-md);
overflow: hidden;
}
/* PX-02 dark-slot activation (extends theme.css's media-query default) */
[data-tg-theme="light"] { --shell-bg: var(--color-surface); --shell-on-surface: var(--color-on-surface); }
[data-tg-theme="dark"] { --shell-bg: var(--color-surface-dark); --shell-on-surface: var(--color-inverse-on-surface); }
[data-tg-theme="dark"] .card { background: var(--color-inverse-surface); color: var(--color-inverse-on-surface); border-color: var(--color-inverse-on-surface); }
[data-tg-theme="dark"] .card-header { border-bottom-color: var(--color-inverse-on-surface); color: var(--color-inverse-on-surface); }
/* NO MapLibre rule here, by design (D-10 / RESEARCH Pitfall 7). */
/* Research Open Question 1 — WebGL2 absent: shell renders over a Rams-tinted field */
body[data-map="absent"] main { background: var(--color-surface-dim); }
/* map register: full-bleed, the dominant surface */
main { position: fixed; inset: 0; }
#map { position: absolute; inset: 0; }
/* MapLibre's stylesheet lands AFTER shell.css in the bundle (map.ts imports
it at module scope), so the Rams chip override needs the #map-scoped
specificity to win the cascade — flat matte-black chip, no translucency. */
#map .maplibregl-ctrl-attrib { border-radius: 0; 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; }
#map .maplibregl-ctrl-attrib a { color: var(--color-pure-white); }
#map .maplibregl-ctrl-attrib-button { background-color: var(--color-matte-black); }
/* top register: status strip */
.strip {
position: absolute; top: calc(var(--safe-top, 0px) + var(--spacing-space-sm)); left: 0; right: 0;
display: flex; align-items: center; justify-content: space-between;
padding: var(--spacing-space-sm) var(--spacing-space-md);
min-height: 32px;
background: var(--color-matte-black); color: var(--color-inverse-on-surface);
font: var(--type-label-sm); letter-spacing: var(--type-label-sm-tracking); text-transform: uppercase;
}
.strip .left { display: flex; align-items: center; gap: var(--spacing-space-xs); }
.strip .lamp { width: 8px; height: 8px; border: 1px solid var(--color-pure-white); }
.strip .lamp[data-state="acquiring"], .strip .lamp[data-state="validating"], .strip .lamp[data-state="awaiting"] { background: var(--color-signal-orange); }
.strip .lamp[data-state="live"] { background: var(--color-olive-green); }
.strip .lamp[data-state="lost"], .strip .lamp[data-state="denied"] { background: var(--color-functional-red); }
.strip .telemetry { font: var(--type-label-sm); }
/* center register: boot / denied / lost card */
.card-stack { position: absolute; left: var(--spacing-margin); right: var(--spacing-margin); top: 50%; transform: translateY(-50%); max-width: 320px; margin-inline: auto; }
.card .gantry { width: 8px; height: 8px; border: 1px solid var(--color-matte-black); }
.card .gantry[data-fill="on"] { background: var(--color-signal-orange); }
.card-actions { display: flex; gap: var(--spacing-space-sm); padding-top: var(--spacing-space-sm); }
/* Device-gate feedback (2026-10-24): shell cards place text elements as direct
.card children — the theme's .card carries no body padding of its own, so
text sat at the card edges. Token-only inline padding on direct children;
the header plate keeps its own. */
.card > .text-headline-md,
.card > .text-body-md,
.card > .text-body-sm { padding-inline: var(--spacing-space-md); }
.card > .text-headline-md { padding-top: var(--spacing-space-sm); }
.card > .text-body-sm:last-child { padding-bottom: var(--spacing-space-sm); }
.card > .card-actions { padding-inline: var(--spacing-space-md); padding-bottom: var(--spacing-space-sm); }
.card > .card-header + .text-headline-md { padding-top: var(--spacing-space-xs); }
/* bottom register: recenter control (40x40 plate, >=44x44 hit target) */
.recenter {
position: absolute; right: var(--spacing-margin); bottom: calc(var(--safe-bottom, 0px) + var(--spacing-space-sm));
width: 40px; height: 40px; padding: 0; border: 0; border-radius: 0;
background: var(--color-matte-black); color: var(--color-pure-white);
display: flex; align-items: center; justify-content: center;
z-index: 2; /* above MapLibre's own chrome; the attrib chip lives bottom-left */
}
.recenter::after { content: ""; position: absolute; inset: -2px; }
.recenter .notch { width: 8px; height: 8px; background: var(--color-signal-orange); }
.recenter[data-follow="free"] .notch { display: none; }
/* UI-SPEC §Follow/Recenter Control, third notch state: live-but-LOST (no fix).
soft-beige, NOT red — red is reserved for the strip/degraded registers; the notch
reads "unlocked, not error" (RESEARCH Pitfall 8 orange discipline). */
.recenter[data-fix="lost"] .notch { background: var(--color-soft-beige); }
.recenter:active { background: var(--color-signal-orange); color: var(--color-matte-black); }
/* position marker (DOM Marker children) */
.pos-accuracy { fill: var(--color-matte-black); fill-opacity: 0.06; stroke: var(--color-matte-black); stroke-opacity: 0.30; stroke-width: 1; }
.pos-dot { fill: var(--color-matte-black); }
.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). */
/* ————— guidance cockpit (Phase 6, 06-UI-SPEC): the maneuver banner, the
compacted strip, the maneuver list, the guidance-card rows + progress bar.
Token vars ONLY (the sanctioned machined-geometry literal list: banner
min-height 96px, arrow plate 48px + glyph 32px, compact strip 24px, row
48px + glyph 24px, next-row lamp 8px, panel max 320px/480px, bar 4px, 1px
rules). States are instant hard swaps — zero motion, zero depth, zero
rounded corners (buttons are radius-reset in the theme base layer). ————— */
/* the banner band: full-bleed matte-black chassis under the safe inset; the
whole banner is the list toggle (the 96px band is the >=44px hit target) */
.guide-banner {
position: absolute; top: calc(var(--safe-top, 0px) + var(--spacing-space-sm)); left: 0; right: 0;
min-height: 96px; padding: var(--spacing-space-sm) var(--spacing-space-md);
display: flex; flex-direction: column; gap: var(--spacing-space-xs);
align-items: stretch; text-align: left; border: 0; font: inherit;
background: var(--color-matte-black); color: var(--color-pure-white);
border-bottom: 1px solid var(--color-chassis-subtle); /* banner→strip separator */
z-index: 3; /* above the list tap-out — the banner stays the toggle (CLOSE) */
}
.guide-banner:focus-visible { outline: 3px solid var(--color-signal-orange); outline-offset: 2px; } /* the route-chip bracket precedent */
.guide-banner .guide-top { display: flex; align-items: center; justify-content: space-between; color: var(--color-chassis-subtle); }
.guide-banner .guide-body { display: flex; align-items: center; gap: var(--spacing-space-sm); flex: 1; min-width: 0; }
.guide-banner .guide-plate { width: 48px; height: 48px; flex: none; display: flex; align-items: center; justify-content: center; background: var(--color-surface-dark); color: var(--color-signal-orange); }
.guide-banner .guide-plate svg { width: 32px; height: 32px; display: block; }
.guide-banner .guide-measures { display: flex; flex-direction: column; gap: var(--spacing-space-xs); flex: 1; min-width: 0; }
.guide-banner .guide-distance-row { display: flex; align-items: baseline; gap: var(--spacing-space-xs); }
.guide-banner .guide-distance { color: var(--color-pure-white); } /* the glance datum — maximum contrast ink */
.guide-banner .guide-unit { color: var(--color-chassis-subtle); }
.guide-banner .guide-street-row { display: flex; align-items: center; gap: var(--spacing-space-xs); min-width: 0; }
.guide-banner .guide-street { color: var(--color-soft-beige); text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.guide-banner .guide-gantry { width: 8px; height: 8px; flex: none; border: 1px solid var(--color-chassis-subtle); }
.guide-banner .guide-gantry[data-fill="on"] { background: var(--color-signal-orange); }
/* strip compaction during guidance — LAYOUT ONLY (the shell root carries
data-guidance="active"); lamp, word and degradation states byte-identical */
[data-guidance="active"] .strip {
top: calc(var(--safe-top, 0px) + var(--spacing-space-sm) + 96px + 1px);
min-height: 24px;
padding-top: var(--spacing-space-xs); padding-bottom: var(--spacing-space-xs);
}
[data-guidance="active"] .strip .telemetry { display: none; } /* accuracy/fix-age are not glance data at speed */
/* the maneuver list: transparent tap-out + card panel over the map —
no dim layer (boot-card precedent); any map tap collapses the list */
.guide-list-wrap { position: absolute; inset: 0; z-index: 2; display: flex; align-items: flex-start; justify-content: center; pointer-events: none; }
.guide-list-tapout { position: absolute; inset: 0; background: transparent; pointer-events: auto; }
.guide-list { pointer-events: auto; position: relative; width: calc(100% - 2 * var(--spacing-margin)); max-width: 320px; max-height: 480px; overflow-y: auto; padding-bottom: var(--spacing-space-sm); margin-top: calc(var(--safe-top, 0px) + var(--spacing-space-sm) + 96px + 1px + 24px + var(--spacing-space-sm)); }
.guide-row { display: flex; align-items: center; gap: var(--spacing-space-sm); min-height: 48px; padding: var(--spacing-space-sm) var(--spacing-space-md); }
.guide-row + .guide-row { border-top: 1px solid var(--color-matte-black); }
[data-tg-theme="dark"] .guide-row + .guide-row { border-top-color: var(--color-inverse-on-surface); }
.guide-row .guide-row-glyph { display: flex; align-items: center; flex: none; color: var(--color-matte-black); }
[data-tg-theme="dark"] .guide-row .guide-row-glyph { color: var(--color-inverse-on-surface); }
.guide-row .guide-row-glyph svg { width: 24px; height: 24px; display: block; }
.guide-row[data-row-state="next"] .guide-row-glyph { color: var(--color-signal-orange); }
.guide-row[data-row-state="past"] .guide-row-glyph { color: var(--color-outline); }
.guide-row .guide-row-street { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.guide-row[data-row-state="past"] .guide-row-street,
.guide-row[data-row-state="past"] .guide-row-dist { color: var(--color-outline); } /* muted history, not hidden */
.guide-row .guide-row-dist { margin-left: auto; flex: none; }
.guide-row .guide-lamp { width: 8px; height: 8px; flex: none; background: var(--color-signal-orange); }
/* guidance-card instruments (composing with the existing .card child rules —
no overrides; the confirm card's bottom placement is the guide-confirm
class hook, read by the mount) */
.guide-progress { height: 4px; margin: var(--spacing-space-xs) var(--spacing-space-md) 0; background: var(--color-chassis-subtle); border: 1px solid var(--color-matte-black); } /* checkbox geometry — distinguishable without color */
[data-tg-theme="dark"] .guide-progress { border-color: var(--color-inverse-on-surface); }
.guide-progress-fill { height: 100%; background: var(--color-signal-orange); }