/* REPAIRMAN — design system
   One scale for space, type, radius and motion across customer / worker / HQ.
   Grammar: Uber (map + one floating card + one button), ENEOS (brand field,
   bespoke icon grid, stacked rounded cards). Light, readable base (Google-app
   clarity: one item per row, quiet gray state text); calm in emergencies. */
:root {
  --bg: #eef2f7;
  --surface: #ffffff;
  --surface2: #e7edf5;
  --surface3: #d3deec;
  --line: #c7d3e2;
  --line-soft: #dfe7f0;
  --text: #0f2140;
  --muted: #51637c;
  --faint: #7e8da6;
  --go: #1e9e5a;
  --warn: #a86f08;
  --err: #d92d20;
  /* brand tokens (2026-11-04, owner decision): the motif is deep navy
     x orange. Navy is the world's foundation (splash, head bands, the
     dock, headings); orange is the accent (primary actions, map pins
     and route, online state, the ring mark, key totals). This replaces
     the 2026-10-29 monochrome conversion. State colours stay reserved
     (--go / --warn / --err). */
  --brand: #0a1e3c;
  --brand-deep: #071630;
  --brand-tint: rgba(10, 30, 60, .08);
  --accent: #ff6a00;
  --accent-deep: #e05d00;
  --accent-tint: rgba(255, 106, 0, .12);
  /* space scale */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 20px; --s6: 24px;
  /* layout gaps (2026-11-02 design-conformance pass): cards and
     sections breathe on ONE rhythm app-wide — the device verdict was
     that boxes sat too close together (was: card margin 10px ad hoc) */
  --gap-card: 14px; --gap-sec: 24px;
  /* type scale */
  --t-label: 11px; --t-meta: 12px; --t-small: 13px; --t-body: 15px; --t-title: 16px; --t-head: 19px;
  /* key totals （合計・お支払い金額・残高）: the number is the hero,
     its label stays small and grey (Skyward number hierarchy) */
  --t-total: 28px;
  /* radius scale */
  --r-sheet: 24px; --r-card: 16px; --r-tile: 16px; --r-input: 12px; --r-btn: 14px;
  /* motion: quiet, Apple-like */
  --ease: cubic-bezier(.32, .72, 0, 1);
  --dur: .28s;
  --shadow-sheet: 0 -4px 14px rgba(10,30,60,.08), 0 16px 40px rgba(10,30,60,.16);
  --shadow-card: 0 1px 3px rgba(10,30,60,.07), 0 4px 14px rgba(10,30,60,.05);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
  background: var(--bg); color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Noto Sans JP", sans-serif;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
  overscroll-behavior: none;
}
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
button:active { transform: scale(.98); }
button { transition: transform .15s var(--ease), background-color .2s, color .2s, opacity .2s; }
input, select, textarea {
  font: inherit; font-size: 16px; color: var(--text); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-input); padding: 12px 14px; width: 100%;
  transition: border-color .2s, box-shadow .2s;
}
input::placeholder, textarea::placeholder { color: var(--faint); }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(10,30,60,.18); }
input.invalid, select.invalid { border-color: var(--err); box-shadow: 0 0 0 3px rgba(229,83,75,.18); }
label { display: block; font-size: var(--t-meta); color: var(--muted); margin: var(--s4) 0 6px; }
.num, .breakdown, .plan .price, .success-code, .big-eta, .facts .v { font-variant-numeric: tabular-nums; }
.ic { display: inline-flex; align-items: center; justify-content: center; flex: none; }
.ic svg { display: block; }

/* ---------- app frame ---------- */
.app { display: flex; flex-direction: column; height: 100%; max-width: 560px; margin: 0 auto; position: relative; background: var(--bg); }
.appicon { width: 30px; height: 30px; border-radius: 9px; object-fit: cover; flex: none; box-shadow: 0 2px 8px rgba(10,30,60,.22); }
/* ---------- brand lockup (one mark, all surfaces) ----------
   Mark tile + wordmark on one baseline, JP name letterspaced beside it.
   Small, quiet, exact — the same lockup on /, /worker and /admin. */
.lockup { display: flex; align-items: center; gap: 10px; min-width: 0; }
.lockup .appicon { width: 32px; height: 32px; border-radius: 10px; box-shadow: 0 2px 10px rgba(10,30,60,.2), inset 0 0 0 1px rgba(10,30,60,.06); }
.lockup-words { display: inline-flex; align-items: center; gap: 9px; white-space: nowrap; }
.wm { font-size: 16px; font-weight: 800; letter-spacing: .045em; line-height: 1; color: var(--text); }
.wm-lg { font-size: 21px; }
.wm-div { width: 1px; height: 13px; background: var(--line); flex: none; }
.wm-jp { font-size: 10px; font-weight: 600; letter-spacing: .24em; line-height: 1; color: var(--muted); }
.head-tag { font-size: var(--t-label); font-weight: 600; letter-spacing: .14em; color: var(--muted); border: 1px solid var(--line); border-radius: 999px; padding: 5px 11px; margin-left: 2px; white-space: nowrap; }
.icon-btn {
  width: 34px; height: 34px; border-radius: 50%; background: var(--surface2);
  display: grid; place-items: center; flex: none; color: var(--text);
}
.screen .head .icon-btn { background: var(--surface); border: 1px solid var(--line-soft); }
.icon-btn:active { background: var(--surface3); }
.main { flex: 1; position: relative; overflow: hidden; }
/* ---------- tab dock: floating glass pill (design pass 2, 2026-11-02) ----------
   Skyward grammar in REPAIRMAN's ink world: a frosted glass bar
   floats over the content; the active tab is a filled ink pill that
   glides behind its icon + label (tabbar.js positions .tab-pill).
   EVERY tab always shows icon + label — the previous MENISCUS bead
   hid inactive labels entirely, so tabs could not be discovered
   (audit C-2/C-3). The dock floats above destination screens
   (z 900 > .screen 800) and hides itself (.dock-off) while flow
   screens (gate / chat / quote …) are open. */
.tabbar {
  display: flex; position: relative; z-index: 900; flex: none;
  height: 64px; margin: 8px 12px calc(env(safe-area-inset-bottom, 0px) + 10px);
  background: rgba(255,255,255,.84);
  -webkit-backdrop-filter: blur(18px) saturate(1.4); backdrop-filter: blur(18px) saturate(1.4);
  border: 1px solid rgba(10,30,60,.06); border-radius: 999px;
  box-shadow: 0 12px 32px rgba(10,30,60,.14), 0 2px 6px rgba(10,30,60,.05);
  user-select: none; -webkit-user-select: none;
  transition: opacity .32s var(--ease), transform .32s var(--ease), visibility 0s;
}
.tabbar.dock-off {
  opacity: 0; visibility: hidden; pointer-events: none; transform: translateY(10px);
  transition: opacity .32s var(--ease), transform .32s var(--ease), visibility 0s .32s;
}
.tab-pill {
  position: absolute; top: 6px; bottom: 6px; left: 0; width: 0;
  background: var(--brand); border-radius: 999px; z-index: 1;
  box-shadow: 0 6px 16px rgba(10,30,60,.25);
  will-change: transform, width;
}
.tabbar button {
  flex: 1; height: 100%; padding: 0; position: relative; z-index: 2;
  color: var(--faint); font-size: 10px; font-weight: 500;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  transition: color .3s var(--ease);
}
.tabbar button .ic { color: var(--faint); transition: color .3s var(--ease); }
.tabbar button .tab-label { line-height: 1; }
.tabbar button.on { color: #fff; font-weight: 700; }
.tabbar button.on .ic { color: #fff; }
.tab-badge {
  position: absolute; top: 6px; right: calc(50% - 19px); min-width: 16px; height: 16px;
  border-radius: 999px; background: var(--brand); color: #fff; font-size: 10px; font-weight: 700;
  display: none; place-items: center; padding: 0 4px; border: 2px solid #fff; z-index: 4;
}
.tab-badge.show { display: grid; }
.tabbar button.on .tab-badge { background: var(--err); }
/* NOTE: the dock-clearance bottom padding for .screen .scroll lives in
   ONE place — the .screen .scroll rule itself (2026-10-29). A second
   rule here once set +92px but was silently overridden by the later
   padding shorthand (+22px), which is why plan / mypage content slid
   under the dock and could never be scrolled clear. Do not re-add a
   separate padding-bottom rule for .screen .scroll. */

/* ---------- map ---------- */
#map { position: absolute; inset: 0; background: #efede8; z-index: 0; }
#map.dark { background: #0d1013; }
.maplibregl-map { font: inherit; }
/* bright vector basemap (OpenFreeMap): keep the attribution legible but quiet */
.maplibregl-ctrl-attrib {
  font-size: 10px; background: rgba(255,255,255,.78) !important; color: rgba(10,30,60,.55) !important;
  padding: 2px 7px; border-radius: 8px;
}
.maplibregl-ctrl-attrib a { color: rgba(10,30,60,.78) !important; }
#map.dark .maplibregl-ctrl-attrib { background: rgba(11,13,16,.55) !important; color: rgba(255,255,255,.65) !important; }
#map.dark .maplibregl-ctrl-attrib a { color: rgba(255,255,255,.85) !important; }
.map-scrim { position: absolute; left: 0; right: 0; z-index: 400; pointer-events: none; }
.map-scrim.top { top: 0; height: 92px; background: linear-gradient(rgba(244,244,246,.6), rgba(244,244,246,0)); }
.map-scrim.bottom { bottom: 0; height: 120px; background: linear-gradient(rgba(244,244,246,0), rgba(244,244,246,.5)); }
.pinwrap { position: relative; width: 40px; height: 40px; }
.pinwrap .halo { position: absolute; inset: 2px; border-radius: 50%; background: rgba(10,30,60,.10); border: 1.5px solid rgba(10,30,60,.30); }
.pinwrap .dotcore { position: absolute; left: 50%; top: 50%; width: 14px; height: 14px; margin: -7px 0 0 -7px; border-radius: 50%; background: var(--brand); border: 3px solid #fff; box-shadow: 0 2px 8px rgba(10,30,60,.35); }

/* Uber-rider grammar: ONE floating pill on the map */
.map-pill {
  position: absolute; top: calc(env(safe-area-inset-top, 0px) + var(--s3)); left: var(--s3); right: var(--s3); z-index: 550;
  display: flex; align-items: center; gap: 10px;
  background: rgba(255,255,255,.93); -webkit-backdrop-filter: blur(16px) saturate(1.3); backdrop-filter: blur(16px) saturate(1.3);
  border: 1px solid var(--line-soft); border-radius: 999px;
  padding: 12px 16px; font-size: var(--t-body); font-weight: 600; text-align: left;
  box-shadow: 0 10px 28px rgba(10,30,60,.15);
}
.map-pill .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--brand); flex: none; }
.map-pill .chev { margin-left: auto; color: var(--faint); display: inline-flex; }

/* map style toggle: bright vector ⇄ dark, quiet glass segment under the pill */
.map-toggle {
  position: absolute; top: calc(env(safe-area-inset-top, 0px) + 67px); right: var(--s3); z-index: 550;
  display: flex; gap: 2px; padding: 3px;
  background: rgba(255,255,255,.93); -webkit-backdrop-filter: blur(16px) saturate(1.3); backdrop-filter: blur(16px) saturate(1.3);
  border: 1px solid var(--line-soft); border-radius: 999px;
  box-shadow: 0 8px 22px rgba(10,30,60,.13);
}
.map-toggle button {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 11px; border-radius: 999px;
  font-size: 11px; font-weight: 600; letter-spacing: .06em; color: var(--muted);
}
.map-toggle button .ic { color: var(--faint); }
.map-toggle button.on { background: var(--surface); color: var(--text); box-shadow: 0 1px 4px rgba(10,30,60,.2); }
.map-toggle button.on .ic { color: var(--brand); }

/* Uber-driver grammar: ONE floating card over the map */
.sheet {
  position: absolute; left: var(--s3); right: var(--s3); bottom: var(--s3); z-index: 600;
  /* solid surface, NO backdrop-filter (ghost fix, 2026-11-01): this
     sheet is a full-width moving layer (drag transforms + inner
     scroll) over a WebGL map. The old stack — translucent white +
     backdrop-filter blur(18px) + will-change: transform on
     .sheet-drag — is the iOS Safari double-paint recipe: during a
     drag/scroll the filtered layer repaints at stale offsets, so
     cards and text render doubled (the owner's device video: gate
     card and launch card ghost-overlapping, hero text doubled).
     The DOM holds exactly one copy (verified); it was compositor
     ghosting. A solid surface reads identically in this monochrome
     world and removes the filtered layer entirely. */
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sheet); padding: 10px var(--s4) var(--s4);
  max-height: 68%; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: none;
  box-shadow: var(--shadow-sheet);
  animation: sheet-in .45s var(--ease) both;
}
/* sheet-in animates OPACITY ONLY — never transform. This element's
   transform is driven by JS inline transitions (the snap glide),
   and a fill-forwards animation that touches transform keeps
   winning the cascade over those transitions: with a from-only
   transform keyframe the implicit "to" resolves to whatever the
   inline transform currently is, so the computed transform is
   always already at its target and every snap JUMPS instead of
   gliding (verified in-engine, 2026-11-02 — part of the owner's
   "the sheet box moves on its own" report). The quiet fade stays;
   the 10px rise does not come back. */
@keyframes sheet-in { from { opacity: 0; } }
.sheet .grab { width: 36px; height: 4px; border-radius: 999px; background: var(--surface3); margin: 0 auto 10px; }
.sheet h2 { font-size: var(--t-title); font-weight: 700; letter-spacing: .01em; }
/* the home question is the hero (design pass 2; audit C-1 — the
   16px heading read as a form label, not as the screen's face) */
.sheet h2.hero-q { font-size: 27px; font-weight: 800; line-height: 1.32; letter-spacing: .01em; margin: 8px 0 4px; }
.sheet .lead { font-size: var(--t-small); color: var(--muted); margin-top: 2px; line-height: 1.6; }

/* ---------- modal bottom sheet (shared component, 2026-10-29) ----------
   One sheet, one job. Used where a detail or a single confirmation is
   more natural layered over the current screen than as a page move:
   the admin request detail and the partner job-completion confirm.
   Motion is the house rule only: a quiet glide on --ease, backdrop
   fade, NO bounce / NO explosive zoom. Close affordances are layered
   on purpose: grab handle (drag down), a × button, backdrop tap, Esc.
   The drag is wired ONLY on the handle/head strip (js/bsheet.js), so
   it never fights the sheet body's own scrolling. */
.bsheet { position: fixed; inset: 0; z-index: 900; visibility: hidden; transition: visibility 0s .5s; }
.bsheet.open { visibility: visible; transition: none; }
.bsheet-backdrop {
  position: absolute; inset: 0; background: rgba(18, 19, 23, .42);
  opacity: 0; transition: opacity .32s var(--ease);
}
.bsheet.open .bsheet-backdrop { opacity: 1; }
.bsheet-panel {
  position: absolute; left: 0; right: 0; bottom: 0; max-height: 88dvh;
  display: flex; flex-direction: column;
  background: var(--surface); border-radius: var(--r-sheet) var(--r-sheet) 0 0;
  box-shadow: var(--shadow-sheet);
  transform: translateY(103%); transition: transform .45s var(--ease);
  padding-bottom: env(safe-area-inset-bottom);
}
.bsheet.open .bsheet-panel { transform: translateY(0); }
.bsheet-panel.dragging { transition: none; }
.bsheet-head { position: relative; padding: 10px 16px 2px; flex: none; touch-action: none; cursor: grab; }
.bsheet-grab { width: 40px; height: 5px; border-radius: 999px; background: var(--surface3); margin: 0 auto; }
.bsheet-close {
  position: absolute; top: 6px; right: 10px; width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 20px; line-height: 1; color: var(--muted); border-radius: 999px;
}
.bsheet-body { overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; padding: 6px var(--s4) var(--s5); }
.bsheet-body .drawer { border: none; box-shadow: none; margin-top: 0; padding: 0; background: transparent; border-radius: 0; }
.bsheet h2 { font-size: var(--t-title); font-weight: 700; }
.bsheet .lead { font-size: var(--t-small); color: var(--muted); margin-top: 4px; line-height: 1.6; }
@media (prefers-reduced-motion: reduce) {
  .bsheet-panel { transition: none; }
  .bsheet-backdrop { transition: none; }
}
.facts { display: flex; gap: var(--s2); margin: var(--s3) 0 2px; }
.facts .fact { flex: 1; background: var(--surface2); border-radius: 12px; padding: 8px 10px; min-width: 0; }
.facts .fact .v { font-size: 14px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.facts .fact .k { font-size: 10px; color: var(--muted); margin-top: 2px; }
/* design pass 2 (audit B-7): the 4-column grid squeezed labels
   into ~75px, so words broke mid-word (バッテリー上 がり). The
   tiles are now two-column horizontal rows — icon disc + a label
   that never wraps (white-space: nowrap is structural: at 375px
   the label box is ~110px and the longest label, バッテリー上がり
   at 13px, needs ~104px). Fewer, bigger, quieter. */
/* minmax(0,1fr), NOT 1fr: a 1fr track's automatic minimum is the
   tile's min-content (glyph + nowrap label), and the two columns'
   minima summed ~29px wider than the sheet's scrollport — which
   silently gave the sheet a HORIZONTAL scroll axis. On the owner's
   iPhone a diagonal scroll then drifted scrollLeft sideways (the
   sheet content sliding left/right, right tiles clipped at the
   sheet edge) and could come to rest with scrollLeft > 0, clipping
   the first glyph of the 「どうしましたか？」 heading (owner's
   device video, 2026-11-02 23:50). The tile trims below (padding,
   glyph, gap) shrink the min-content demand so the longest label
   (バッテリー上がり) still fits whole inside a half column, and
   the min-width:0 / ellipsis pair is the hard backstop: a longer
   future label ellipsizes inside its own tile instead of ever
   widening the page again. */
.tilegrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 14px 0 4px; }
.tile {
  background: #fff; border: none; border-radius: 16px;
  box-shadow: var(--shadow-card);
  padding: 12px 8px; display: flex; flex-direction: row; align-items: center; gap: 7px;
  text-align: left; min-width: 0;
  transition: transform .15s var(--ease), background-color .2s;
}
.tile:active { background: var(--surface2); }
.tile .glyph {
  flex: none; width: 33px; height: 33px; border-radius: 50%; background: var(--brand-tint);
  display: grid; place-items: center; color: var(--brand);
}
.tile .tl { font-size: 12px; font-weight: 600; color: var(--text); white-space: nowrap; line-height: 1.3; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.text-link { display: block; width: 100%; text-align: center; padding: 10px; color: var(--muted); font-size: 14px; margin-top: 2px; }
.big-eta { font-size: 50px; font-weight: 800; line-height: 1.05; letter-spacing: -.01em; }
.big-eta small { font-size: 19px; font-weight: 700; margin-left: 2px; }
.eta-label { font-size: var(--t-meta); color: var(--muted); margin-bottom: 2px; display: flex; align-items: center; gap: 6px; }
.btn-primary {
  display: flex; align-items: center; justify-content: center; gap: var(--s2);
  width: 100%; background: var(--accent); color: #fff;
  font-weight: 700; font-size: var(--t-title); letter-spacing: .02em;
  border-radius: 999px; padding: 15px var(--s4); text-align: center; margin-top: var(--s3);
  box-shadow: 0 8px 22px rgba(255,106,0,.30), 0 2px 6px rgba(255,106,0,.18);
  transition: background-color .2s, box-shadow .25s, transform .15s var(--ease), opacity .2s;
}
.btn-primary:active { background: var(--accent-deep); }
.btn-primary:disabled { opacity: .5; box-shadow: none; }
.btn-primary.loading { pointer-events: none; opacity: .85; }
.btn-ghost {
  display: flex; align-items: center; justify-content: center; gap: var(--s2);
  width: 100%; background: var(--surface2); color: var(--text);
  font-weight: 600; font-size: var(--t-body); border-radius: var(--r-btn); padding: 13px; text-align: center; margin-top: 10px;
  border: 1px solid var(--line);
  transition: background-color .2s, transform .15s var(--ease), opacity .2s;
}
.spinner { width: 16px; height: 16px; border-radius: 50%; border: 2px solid rgba(255,255,255,.35); border-top-color: #fff; animation: spin .7s linear infinite; flex: none; }
@keyframes spin { to { transform: rotate(360deg); } }
.chips { display: flex; flex-wrap: wrap; gap: var(--s2); margin: 6px 0 4px; }
.chip {
  padding: 9px 14px; border-radius: 999px; background: var(--surface2);
  border: 1px solid var(--line); font-size: 14px; transition: transform .15s var(--ease), background-color .2s;
}
.chip:active { background: var(--surface3); }
.chip.on { background: var(--brand); color: #fff; border-color: var(--brand); font-weight: 700; }
/* big option rows (consultation questionnaire + category choice):
   answers are taps on full-width visible options, typing is the
   last resort — the meal-app grammar KUDAKA pointed at 2026-10-31 */
.chip.big {
  flex: 1 1 100%; text-align: left; padding: 13px 16px;
  border-radius: var(--r-btn); font-size: 15px; font-weight: 600;
}
/* consultation progress: a slim bar under the chat header that
   fills as answers land, paired with the header's あと◯つ text */
.pbar { height: 3px; background: var(--line-soft); flex: none; }
.pbar-fill { height: 100%; width: 0; background: var(--brand); transition: width .35s var(--ease); }

/* ---------- overlay screens (cover the whole app, like a pushed page) ---------- */
.screen {
  position: absolute; inset: 0; z-index: 800; background: var(--bg);
  display: none; flex-direction: column;
}
.screen.open { display: flex; animation: scr-in .32s var(--ease) both; }
/* entry/exit glide by TRANSFORM ONLY (ghost fix, 2026-10-31): scr-in
   used to fade opacity 0 -> 1 and scr-out 1 -> 0. A screen is an
   opaque full-viewport layer over a fully mounted previous screen
   (or home), so every fade was a crossfade — two screens' text
   superimposed for ~100-250ms per navigation (measured; KUDAKA's
   device recording showed the double exposure throughout). The
   glide keeps the same quiet vocabulary (10px rise in, 7px settle
   out, then the cut) but at full opacity, so exactly one screen is
   readable at every frame and no text ever blends. */
@keyframes scr-in { from { transform: translateY(10px); } }
/* exit glide (motion pass, 2026-10-28): screens used to vanish the
   instant .open was removed — a hard web cut. customer.js now adds
   .closing first and removes .open after the glide, so a screen
   settles out the same quiet way it came in. */
.screen.closing { animation: scr-out .19s var(--ease) both; pointer-events: none; }
@keyframes scr-out { to { transform: translateY(7px); } }
/* bottom padding = floating-dock clearance, unified here for every
   destination screen (plans / mypage / notifications / info …):
   dock footprint is 64 height + 8 top margin + 10 bottom margin = 82px
   above the safe-area inset; +100px leaves the last element fully
   visible and tappable above the bar (2026-10-29 owner report). */
.screen .scroll { flex: 1; min-height: 0; overflow-y: auto; padding: var(--s4) 18px calc(env(safe-area-inset-bottom, 0px) + 100px); -webkit-overflow-scrolling: touch; overscroll-behavior: none; }
.screen .head {
  display: flex; align-items: center; gap: 10px;
  padding: calc(env(safe-area-inset-top, 0px) + 8px) 14px 8px;
  border-bottom: 1px solid var(--line-soft); background: rgba(255,255,255,.96);
}
.screen .head .t { font-size: var(--t-body); font-weight: 700; }
.screen .head .spacer { flex: 1; }
.screen .head .progress { font-size: var(--t-meta); color: var(--faint); font-variant-numeric: tabular-nums; }
/* consultation read-aloud toggle (chat head, far right end): on is the
   plain speaker in ink; off swaps to the slashed speaker in faint grey,
   so the state reads at a glance without any label text */
.screen .head .tts-toggle { flex: none; transition: color .2s; }
.screen .head .tts-toggle[aria-pressed="false"] { color: var(--faint); }

/* chat */
.chat { display: flex; flex-direction: column; gap: var(--s2); }
.msg { max-width: 85%; padding: 10px 13px; border-radius: 16px; font-size: var(--t-body); line-height: 1.55; animation: msg-in .26s var(--ease) both; }
@keyframes msg-in { from { opacity: 0; transform: translateY(5px); } }
/* design pass 2 (owner 2026-11-02): long formal bubbles read stiff.
   AI replies now read as plain conversation text — no bubble box —
   split into short sentence-group parts by addMsg; the member's own
   words keep the ink bubble so the two voices stay distinct. */
.msg.ai { background: transparent; align-self: flex-start; max-width: 94%; padding: 3px 2px; border-radius: 0; }
.msg.ai.split-part { margin-top: calc(var(--s2) * -1 + 3px); }
.msg.me { background: var(--text); color: #fff; align-self: flex-end; border-bottom-right-radius: 6px; font-weight: 600; }
/* AI reply-wait indicator (owner direction 2026-10-30): the house
   standard for every AZRION AI consultation — three ink dots that
   rise and glow in turn, the same grammar as the Muse app's typing
   display. When the reply lands the bubble fades out over the real
   one. Motion spec is shared with A'ziter: 6px dots, gap 4px, 1.3s
   cycle, delays 0/.18/.36s, -3px rise + fade .3→1, ease-in-out. */
.msg.typing { display: flex; align-items: center; padding: 13px 15px; background: var(--surface2); border-radius: 16px; border-bottom-left-radius: 6px; max-width: 85%; }
.tdots { display: inline-flex; gap: 4px; }
.tdots i { width: 6px; height: 6px; border-radius: 50%; background: var(--text); animation: tdot 1.3s ease-in-out infinite; }
.tdots i:nth-child(2) { animation-delay: .18s; }
.tdots i:nth-child(3) { animation-delay: .36s; }
@keyframes tdot { 0%, 60%, 100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
@media (prefers-reduced-motion: reduce) { .tdots i { animation: none; opacity: .55; } }
.skl { display: block; height: 10px; border-radius: 5px; background: var(--surface3); animation: sklPulse 1.5s ease-in-out infinite alternate; }
@keyframes sklPulse { from { opacity: .45; } to { opacity: .95; } }
@media (prefers-reduced-motion: reduce) { .skl { animation: none; } }
/* flow divider: the in-conversation marker that the intake is
   complete and the flow is moving on to the quote */
.flow-divider { align-self: center; display: flex; align-items: center; gap: 9px; background: var(--surface); border: 1px solid var(--line-soft); border-radius: 14px; padding: 10px 16px; margin: 4px 0; box-shadow: var(--shadow-card); animation: msg-in .3s var(--ease) both; }
.flow-divider .fd-check { width: 22px; height: 22px; flex: none; border-radius: 50%; background: var(--ink); color: #fff; display: grid; place-items: center; }
.flow-divider .fd-text { display: flex; flex-direction: column; }
.flow-divider .fd-title { font-size: 13.5px; font-weight: 700; line-height: 1.3; }
.flow-divider .fd-sub { font-size: 11.5px; color: var(--muted); margin-top: 1px; }
/* quote generation staging: skeleton bars stand in for the summary /
   breakdown while the quote "generates", then the cards glide in */
.qskl { display: flex; flex-direction: column; gap: 9px; padding: 5px 0 3px; }
.qskl .skl { height: 11px; }
.card.preparing .voice-btn, .card.preparing .text-link { visibility: hidden; }
.quote-reveal { animation: qIn .45s var(--ease) both; }
@keyframes qIn { from { opacity: 0; transform: translateY(8px); } }
/* consultation big options live in the scroll flow (see setChips in
   customer.js): a vertical stack of full-width rows directly under
   the question, reached by the conversation's own scroll */
.chat-options { display: flex; flex-direction: column; gap: var(--s2); margin-top: var(--s2); }
.chat-options:empty { display: none; }
.chat-options .chip.big { flex: none; width: 100%; }
.chat-input { flex: none; border-top: 1px solid var(--line-soft); padding: 10px 12px calc(env(safe-area-inset-bottom, 0px) + 10px); background: var(--surface); }
.chat-input .row { display: flex; gap: var(--s2); align-items: center; }
.chat-input input { flex: 1; border-radius: 999px; padding: 11px 16px; }
.chat-input .send {
  width: 40px; height: 40px; flex: none; border-radius: 50%; background: var(--brand); color: #fff;
  display: grid; place-items: center; transition: opacity .2s, transform .15s var(--ease), background-color .2s;
}
.chat-input .send:disabled { background: var(--surface3); color: var(--faint); }
/* voice input (Phase 5): one big target, recording state unmissable */
.chat-input .mic-btn { flex: none; width: 48px; height: 48px; border-radius: 999px; border: 1.5px solid var(--line); background: var(--surface); color: var(--ink); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.chat-input .mic-btn.recording { background: #b3261e; border-color: #b3261e; color: #fff; animation: micPulse 1.1s ease-in-out infinite; }
@keyframes micPulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(179, 38, 30, .45); } 50% { box-shadow: 0 0 0 9px rgba(179, 38, 30, 0); } }
.voice-state { margin-top: 8px; font-size: 13px; font-weight: 700; color: #b3261e; }
/* consultation photo bubble: the attached image itself is the message */
.msg.me.photo { padding: 5px; background: var(--surface2); }
.msg.me.photo img { display: block; width: 180px; max-width: 55vw; border-radius: 12px; }
/* read-aloud: the chat reads AI replies automatically (toggle in the
   chat head, .tts-toggle above); this pill remains only as the quote
   screen's explicit "read the summary" action */
.voice-btn { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line); background: var(--surface); color: var(--ink); border-radius: 999px; padding: 7px 13px; font-size: 12.5px; font-weight: 700; cursor: pointer; min-height: 36px; }
.voice-btn.speaking { background: var(--ink); color: var(--surface); }
/* identity verification blocks (register steps / profile) */
.verify-row { border: 1px solid var(--line-soft); border-radius: 12px; padding: 12px 14px; margin-top: 10px; }
.verify-row .v-title { font-weight: 700; font-size: 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.verify-chip { font-size: 11px; font-weight: 700; border-radius: 999px; padding: 2px 9px; background: var(--surface3); color: var(--sub); }
.verify-chip.ok { background: #e5f3e8; color: #176c2b; }
.verify-chip.wait { background: #fdf0e2; color: #9a5b12; }
.verify-row .v-note { font-size: 12.5px; color: var(--sub); margin-top: 5px; line-height: 1.55; }
.verify-row .v-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; align-items: center; }
.verify-row .v-actions input { flex: 1; min-width: 130px; }
.verify-row .v-actions button { min-height: 42px; }
input[readonly] { background: var(--surface3); color: var(--sub); }
/* first-run prompt (Phase 6 pattern): install first on iOS, then
   notification permission — once only, dismissible, never blocking */
.notify-prompt { position: relative; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); padding: 14px 16px; margin: 0 0 12px; animation: np-in .35s var(--ease) both; }
@keyframes np-in { from { opacity: 0; transform: translateY(6px); } }
@media (prefers-reduced-motion: reduce) { .notify-prompt { animation: none; } }
.notify-prompt .np-title { font-weight: 700; font-size: 14.5px; padding-right: 34px; }
.notify-prompt .np-body { font-size: 12.5px; color: var(--sub); line-height: 1.6; margin-top: 5px; }
.notify-prompt .np-actions { margin-top: 10px; }
.notify-prompt .np-close { position: absolute; top: 8px; right: 8px; width: 34px; height: 34px; border: none; background: transparent; color: var(--sub); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; border-radius: 999px; }
.notify-prompt .np-actions-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.notify-prompt .np-later { border: none; background: transparent; color: var(--sub); font-size: 12.5px; font-weight: 600; cursor: pointer; padding: 8px 6px; font-family: inherit; }
.notify-prompt .np-never { margin-left: auto; border: none; background: transparent; color: var(--sub); font-size: 11.5px; cursor: pointer; padding: 8px 2px; font-family: inherit; text-decoration: underline; text-underline-offset: 2px; }
/* footer pill sets stay compact by construction, but cap the box so
   no future many-pill set can ever re-crowd the log: past the cap the
   box scrolls itself (scroll-lock resolves it via data-sl-scroll) */
.chat-input .chips { margin: 0 0 var(--s2); max-height: 38vh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }

/* quote / cards / lists */
.card { background: var(--surface); border: 1px solid rgba(10,30,60,.045); border-radius: var(--r-card); padding: var(--s4); margin-bottom: var(--gap-card); box-shadow: var(--shadow-card); }
.card .rowline { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.card .name { font-size: var(--t-body); font-weight: 700; }
.card .meta { font-size: var(--t-meta); color: var(--muted); margin-top: 3px; line-height: 1.55; }
.card .chev { color: var(--faint); display: inline-flex; }
button.card, .card.tap { width: 100%; text-align: left; }
.breakdown { margin: 6px 0; }
.breakdown .line { display: flex; justify-content: space-between; align-items: baseline; padding: 8px 0; border-bottom: 1px solid var(--line-soft); font-size: var(--t-body); }
.breakdown .line span:first-child { color: var(--muted); font-size: 14px; }
.breakdown .total { display: flex; justify-content: space-between; align-items: baseline; padding: 12px 0 2px; font-size: var(--t-small); font-weight: 600; color: var(--muted); }
.breakdown .total > span:last-child { font-size: var(--t-total); font-weight: 800; color: var(--text); letter-spacing: -.01em; }
.sumrow { display: flex; justify-content: space-between; gap: var(--s3); padding: 7px 0; border-bottom: 1px solid var(--line-soft); font-size: 14px; }
.sumrow:last-child { border-bottom: none; }
.sumrow .k { color: var(--muted); flex: none; font-size: var(--t-small); }
.sumrow .v { text-align: right; }
.status-badge { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 999px; font-size: var(--t-label); font-weight: 700; background: var(--surface2); border: 1px solid var(--line); flex: none; }
.status-badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--muted); }
.status-badge.go { color: var(--go); border-color: rgba(47,191,113,.5); }
.status-badge.go::before { background: var(--go); }
.status-badge.warn { color: var(--warn); border-color: rgba(232,178,58,.5); }
.status-badge.warn::before { background: var(--warn); }
.status-badge.err { color: var(--err); border-color: rgba(229,83,75,.5); }
.status-badge.err::before { background: var(--err); }

/* timeline with a rail */
.timeline { margin: var(--s4) 0; position: relative; }
.timeline::before { content: ""; position: absolute; left: 5px; top: 12px; bottom: 12px; width: 2px; background: var(--line-soft); border-radius: 2px; }
.timeline .step { display: flex; gap: var(--s3); padding: 7px 0; color: var(--faint); font-size: 14px; align-items: center; position: relative; }
.timeline .dot { width: 12px; height: 12px; border-radius: 50%; background: var(--surface3); flex: none; z-index: 1; }
.timeline .step.done { color: var(--muted); }
.timeline .step.done .dot { background: var(--go); }
.timeline .step.now { color: var(--text); font-weight: 700; }
.timeline .step.now .dot { background: var(--go); box-shadow: 0 0 0 5px rgba(47,191,113,.16); }

.plan { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s3); }
.plan > div:first-child { min-width: 0; }
/* price never wraps (2026-10-29 owner report: the 「月」 of ¥330/月
   dropped alone onto a second line): the number is the hero, the unit
   rides small on the same baseline, and the name/meta side shrinks
   instead of squeezing the price. */
.plan .price { font-size: 24px; font-weight: 800; flex: none; white-space: nowrap; display: flex; align-items: baseline; gap: 2px; }
.plan .price small { font-size: 12px; color: var(--muted); font-weight: 600; }
.plan-actions { display: flex; gap: var(--s2); margin-top: var(--s3); }
.plan-actions .btn-primary, .plan-actions .btn-ghost { flex: 1; min-height: 44px; font-size: var(--t-small); }
.note { font-size: var(--t-meta); color: var(--muted); line-height: 1.7; }
/* label/value rows for profile & registration summaries (2026-10-29):
   the NTJ account page's clarity pattern — one fact per row, label in
   muted small text, value beside it — instead of packing several
   facts into one separator-joined line. */
.kv-rows { display: grid; gap: 4px; margin-top: 6px; }
.kv-row { display: flex; gap: 10px; font-size: var(--t-meta); line-height: 1.6; }
.kv-row .k { color: var(--muted); flex: none; min-width: 76px; }
.kv-row .v { color: var(--text); word-break: break-all; }
.err-text { color: var(--err); font-size: var(--t-small); margin-top: var(--s2); min-height: 18px; line-height: 1.5; }
.ok-text { color: #176c2b; font-size: var(--t-small); line-height: 1.5; transition: opacity .5s ease; }
.success-code { font-size: 29px; font-weight: 800; letter-spacing: .1em; text-align: center; margin: 8px 0 4px; }
.center { text-align: center; }
.hidden { display: none !important; }
.updated-at { font-size: var(--t-label); color: var(--faint); }

/* empty state */
.empty { text-align: center; padding: 26px var(--s4); }
.empty .eic { width: 52px; height: 52px; border-radius: 50%; background: var(--surface2); display: grid; place-items: center; margin: 0 auto 10px; color: var(--faint); }
.empty .et { font-size: 14px; font-weight: 600; }
.empty .es { font-size: var(--t-meta); color: var(--muted); margin-top: 4px; line-height: 1.65; }

/* success emblem */
.emblem { width: 64px; height: 64px; border-radius: 50%; background: rgba(47,191,113,.13); color: var(--go); display: grid; place-items: center; margin: 18px auto 6px; }

/* skeleton */
.skel { border-radius: 12px; background: linear-gradient(100deg, var(--surface2) 40%, #d9dde3 50%, var(--surface2) 60%); background-size: 200% 100%; animation: sk 1.5s ease-in-out infinite; height: 72px; margin-bottom: 10px; }
@keyframes sk { to { background-position: -200% 0; } }

/* ---------- standalone pages (worker / HQ) ---------- */
.page-wrap { max-width: 560px; margin: 0 auto; padding: 0 18px 56px; height: 100%; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.page-head { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 34px 0 8px; }
.page-head .appicon { width: 56px; height: 56px; border-radius: 15px; }
.page-head .lockup-words { margin-top: 13px; }
.page-head h1 { font-size: var(--t-head); font-weight: 700; margin-top: 14px; letter-spacing: .02em; }
.page-head .sub { font-size: var(--t-small); color: var(--muted); margin-top: 6px; line-height: 1.65; max-width: 30em; }
.admin-wrap { max-width: 860px; margin: 0 auto; padding: 18px 16px 60px; height: 100%; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.admin-head { display: flex; align-items: center; gap: 10px; margin-bottom: 2px; }
.seg { display: flex; gap: var(--s2); margin: 14px 0; }
.seg button { padding: 8px 15px; border-radius: 999px; background: var(--surface2); border: 1px solid var(--line); font-size: 14px; }
.seg button.on { background: var(--text); color: #fff; font-weight: 700; border-color: var(--text); }
.drawer { background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-card); padding: var(--s4); margin-top: var(--s3); box-shadow: var(--shadow-card); }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
@media (max-width: 640px) { .grid2 { grid-template-columns: 1fr; } }
select { appearance: none; }

/* ---------- member gate (register / login) ----------
   One screen, one job: sign in. Small quiet identity lockup, one segmented
   switch with a sliding thumb, large filled fields, one large action.
   No explanatory prose — labels and the action carry the screen. */
/* The gate's world (2026-11-02, glass-login pass per the 2026-10-31
   decision): a quiet street-grid backdrop in REPAIRMAN's own greys —
   the map world the member returns to — with the form floating over
   it as a frosted-glass card (see #screenAuth .auth-wrap below). The
   old neumorphic plate block that used to sit here was dead CSS
   (every declaration overridden by the gate-specific rule); removed. */
#screenAuth { z-index: 900; background:
  radial-gradient(120% 60% at 85% -5%, rgba(255, 255, 255, .9), rgba(255, 255, 255, 0) 60%),
  radial-gradient(90% 50% at -10% 105%, rgba(10, 30, 60, .10), rgba(10, 30, 60, 0) 55%),
  repeating-linear-gradient(0deg, rgba(10, 30, 60, .045) 0 1px, transparent 1px 56px),
  repeating-linear-gradient(90deg, rgba(10, 30, 60, .045) 0 1px, transparent 1px 56px),
  linear-gradient(180deg, #f2f3f5, #e8eaed);
}
/* phone gate (2026-11-02): same gate world as the auth screen, one
   step higher — it must cover everything until the number is in. */
#screenPhoneGate { z-index: 950; background:
  radial-gradient(120% 60% at 85% -5%, rgba(255, 255, 255, .9), rgba(255, 255, 255, 0) 60%),
  radial-gradient(90% 50% at -10% 105%, rgba(10, 30, 60, .10), rgba(10, 30, 60, 0) 55%),
  repeating-linear-gradient(0deg, rgba(10, 30, 60, .045) 0 1px, transparent 1px 56px),
  repeating-linear-gradient(90deg, rgba(10, 30, 60, .045) 0 1px, transparent 1px 56px),
  linear-gradient(180deg, #f2f3f5, #e8eaed);
}
#screenPhoneGate .field { margin-top: 18px; }
#screenPhoneGate label { margin: 0 0 8px; font-size: var(--t-meta); font-weight: 600; letter-spacing: .05em; }
#screenPhoneGate input {
  height: 56px; padding: 0 16px; border-radius: 14px; width: 100%;
  border: 1px solid rgba(255, 255, 255, .55); background: #edeef0;
  box-shadow: inset 3px 3px 7px rgba(163, 177, 198, .42), inset -3px -3px 7px rgba(255, 255, 255, .95);
}
#screenPhoneGate .err-text { margin-top: 14px; min-height: 20px; }
.auth-wrap { width: 100%; max-width: 400px; margin: 0 auto; padding-top: clamp(24px, 7vh, 64px); }
.auth-head { display: flex; flex-direction: column; align-items: center; text-align: center; }
.auth-head .appicon { width: 56px; height: 56px; border-radius: 16px; }
.auth-head .lockup-words { margin-top: 14px; }
#screenAuth.open .auth-head { animation: auth-rise .45s var(--ease) both; }
#screenAuth.open .auth-seg { animation: auth-rise .45s var(--ease) .06s both; }
@keyframes auth-rise { from { opacity: 0; transform: translateY(10px); } }
.auth-seg { position: relative; display: flex; padding: 4px; margin: 30px 0 4px; background: #e8eaed; border-radius: 999px; box-shadow: inset 2px 2px 5px rgba(163, 177, 198, .40), inset -2px -2px 5px rgba(255, 255, 255, .9); }
.auth-seg button {
  position: relative; z-index: 1; flex: 1; padding: 10px 0;
  border-radius: 999px; font-size: 14px; font-weight: 600; color: var(--muted); text-align: center;
  transition: color .25s;
}
.auth-seg button.on { color: var(--text); font-weight: 700; }
.auth-thumb {
  position: absolute; top: 4px; left: 4px; width: calc(50% - 4px); height: calc(100% - 8px);
  border-radius: 999px; background: var(--surface);
  box-shadow: 0 1px 3px rgba(10,30,60,.18), 0 5px 14px rgba(10,30,60,.1);
  transition: transform .38s var(--ease);
}
.auth-seg.reg .auth-thumb { transform: translateX(100%); }
#authLoginBox, #authRegisterBox { animation: auth-box-in .3s var(--ease) both; }
@keyframes auth-box-in { from { opacity: 0; transform: translateY(6px); } }
/* phone gate (2026-11-02): covers the whole partner app until a
   Google-created account registers its phone number. */
#wPhoneGate {
  position: fixed; inset: 0; z-index: 960; overflow-y: auto;
  display: grid; place-items: center; padding: 20px;
  background:
    radial-gradient(120% 60% at 85% -5%, rgba(255, 255, 255, .9), rgba(255, 255, 255, 0) 60%),
    repeating-linear-gradient(0deg, rgba(10, 30, 60, .045) 0 1px, transparent 1px 56px),
    repeating-linear-gradient(90deg, rgba(10, 30, 60, .045) 0 1px, transparent 1px 56px),
    linear-gradient(180deg, #f2f3f5, #e8eaed);
}
#wPhoneGate.hidden { display: none; }
/* Google sign-in (2026-11-02): the standard white G button, kept to
   Google's own shape (white field, multicolor G, quiet border) so it
   reads as what it is on every surface; hidden until
   /api/auth/google/config says the secrets are in place. The divider
   and button live inside the auth boxes; spacing follows the auth
   rhythm (14px) rather than inventing its own. */
.google-or { display: flex; align-items: center; gap: 12px; margin: 16px 2px 0; color: var(--faint); font-size: 12px; }
.google-or::before, .google-or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.google-btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; height: 52px; margin-top: 14px; padding: 0 16px;
  background: #fff; color: #1f1f1f; border: 1px solid #dadce0; border-radius: 999px;
  font-size: 15px; font-weight: 600; text-decoration: none;
  box-shadow: 0 1px 2px rgba(10, 30, 60, .08);
  transition: background-color .2s, box-shadow .2s;
}
.google-btn:active { background: #f8f9fa; box-shadow: none; }
.google-btn svg { flex: none; }
#screenAuth .field { margin-top: 18px; }
#screenAuth label { margin: 0 0 8px; font-size: var(--t-meta); font-weight: 600; letter-spacing: .05em; }
#screenAuth input {
  height: 56px; padding: 0 16px; border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, .55); background: #edeef0;
  box-shadow: inset 3px 3px 7px rgba(163, 177, 198, .42), inset -3px -3px 7px rgba(255, 255, 255, .95);
  transition: border-color .2s, box-shadow .2s, background-color .2s;
}
#screenAuth input:focus { background: #f4f5f7; border-color: var(--brand); box-shadow: inset 3px 3px 7px rgba(163, 177, 198, .35), inset -3px -3px 7px rgba(255, 255, 255, .95), 0 0 0 3px rgba(255, 106, 0, .18); }
#screenAuth input.invalid { background: #f4f5f7; border-color: var(--err); box-shadow: inset 3px 3px 7px rgba(163, 177, 198, .35), inset -3px -3px 7px rgba(255, 255, 255, .95), 0 0 0 3px rgba(217, 45, 32, .12); }
#screenAuth .err-text { margin-top: 14px; min-height: 20px; }
.pass-wrap { position: relative; }
.pass-wrap input { padding-right: 54px; }
.pass-eye {
  position: absolute; top: 50%; right: 7px; transform: translateY(-50%);
  width: 42px; height: 42px; border-radius: 50%; display: grid; place-items: center; color: var(--faint);
}
.pass-eye:active { transform: translateY(-50%) scale(.94); background: rgba(10,30,60,.05); }
/* ---------- password strength meter (brand-EC presentation) ---------- */
.pw-meter { margin-top: 8px; }
.pw-meter-bar { height: 6px; overflow: hidden; border-radius: 999px; background: var(--surface2); }
.pw-meter-fill { display: block; height: 100%; width: 0; border-radius: 999px; background: var(--line); transition: width .3s ease, background-color .3s ease; }
.pw-meter-fill.pw-weak { background: #d92d20; }
.pw-meter-fill.pw-mid { background: #e6a817; }
.pw-meter-fill.pw-strong { background: #1f9d55; }
.pw-meter-label { margin: 6px 0 0; font-size: var(--t-meta); font-weight: 600; min-height: 15px; }
.pw-meter-label.pw-weak { color: var(--err); }
.pw-meter-label.pw-mid { color: #8a6108; }
.pw-meter-label.pw-strong { color: #14713c; }
.pw-guide { margin: 4px 0 0; font-size: 11px; line-height: 1.6; color: var(--faint); }

/* ---------- VERSO: blade sweep for the gate's login/register switch ----------
   A skewed ink blade crosses the gate; at full cover the two panes trade
   places (customer.js applyAuthTab), then the blade exits and the new
   pane settles in (.verso-in). One pale seam of light rides the blade's
   leading edge (monochrome since the 2026-10-28 gate pass). */
.verso-blade {
  position: absolute; top: 0; bottom: 0; left: -8%; right: -8%; z-index: 60;
  background: #0a1e3c; overflow: hidden; pointer-events: none;
  visibility: hidden; transform: translateX(112%) skewX(-7deg);
  transition: transform .38s var(--ease), visibility 0s .45s;
}
.verso-blade.show { visibility: visible; transition: transform .38s var(--ease); pointer-events: auto; }
.verso-blade.from-left { transform: translateX(-112%) skewX(-7deg); }
.verso-blade.cover { transform: translateX(0) skewX(-7deg); }
.verso-blade.exit { transform: translateX(-112%) skewX(-7deg); }
.verso-blade.from-left.exit { transform: translateX(112%) skewX(-7deg); }
.verso-seam {
  position: absolute; top: 0; bottom: 0; left: 0; width: 3px;
  background: #fff; box-shadow: 0 0 14px rgba(255, 255, 255, .35);
}
.verso-blade.from-left .verso-seam { left: auto; right: 0; }
.verso-inner {
  position: absolute; inset: 0; transform: skewX(7deg);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
}
.verso-kicker { font-size: 10px; font-weight: 700; letter-spacing: .34em; color: rgba(255,255,255,.55); }
.verso-title { font-size: 30px; font-weight: 800; letter-spacing: .05em; color: #fff; }
.verso-in { animation: verso-in .45s var(--ease) both; }
@keyframes verso-in { from { opacity: 0; transform: translateY(8px); } }

/* ---------- Aguante: hold-to-confirm (payment only) ----------
   Outline-style button; a brand-orange liquid rises while held, the
   label reads white through the liquid (tinta layer clipped to the
   fill), % counter at the right. Release early -> drains, no action. */
.btn-primary.ag-btn {
  position: relative; overflow: hidden; background: var(--surface);
  color: var(--brand-deep); border: 1.5px solid var(--brand);
  box-shadow: none; touch-action: none; user-select: none; -webkit-user-select: none;
  -webkit-touch-callout: none; margin-top: 0;
}
.btn-primary.ag-btn:active { background: var(--surface); }
.btn-primary.ag-btn:disabled { opacity: 1; }
.ag-liquid {
  position: absolute; left: 0; right: 0; bottom: 0; height: 0; z-index: 0;
  background: linear-gradient(180deg, #43464d, var(--brand));
}
.ag-liquid::before {
  content: ""; position: absolute; top: -5px; left: -10%; right: -10%; height: 10px;
  background: inherit; border-radius: 50%;
  animation: ag-wave 2.6s ease-in-out infinite alternate;
}
@keyframes ag-wave { from { transform: translateX(-7px) scaleX(1.05); } to { transform: translateX(7px) scaleX(1.05); } }
.ag-label { position: relative; z-index: 1; }
.ag-tinta {
  position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; justify-content: center;
  color: #fff; clip-path: inset(100% 0 0 0); pointer-events: none;
}
.ag-pct {
  position: absolute; right: 16px; top: 50%; transform: translateY(-50%); z-index: 3;
  font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--brand-deep);
  min-width: 3em; text-align: right; pointer-events: none;
}
.ag-btn.ag-done { border-color: var(--brand-deep); }
.pay-block { margin-top: 14px; }
.pay-hint { margin: 8px 2px 0; font-size: 11px; line-height: 1.6; color: var(--faint); text-align: center; }
.auth-cta { height: 56px; border-radius: 16px; margin-top: var(--s2); }
.btn-primary.loading::before {
  content: ""; width: 16px; height: 16px; flex: none; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.35); border-top-color: #fff; animation: spin .7s linear infinite;
}
.auth-note { font-size: var(--t-meta); color: var(--faint); line-height: 1.7; text-align: center; margin-top: 14px; }

/* ---------- my page ---------- */
.prof-head { display: flex; align-items: center; gap: var(--s3); }
.avatar {
  width: 48px; height: 48px; border-radius: 50%; flex: none;
  background: var(--brand-tint); color: var(--brand-deep);
  display: grid; place-items: center; font-size: 19px; font-weight: 800;
}
.prof-head .pname { font-size: var(--t-title); font-weight: 700; }
.prof-head .pmeta { font-size: var(--t-meta); color: var(--muted); margin-top: 2px; }
.sec-title { font-size: var(--t-small); font-weight: 700; color: var(--muted); letter-spacing: .06em; margin: var(--gap-sec) 2px 10px; }
.setrow {
  display: flex; align-items: center; gap: var(--s3); width: 100%; text-align: left;
  padding: 13px 2px; border-bottom: 1px solid var(--line-soft); font-size: var(--t-body);
}
.setrow:last-child { border-bottom: none; }
.setrow .grow { flex: 1; min-width: 0; }
.setrow .sub { display: block; font-size: var(--t-meta); color: var(--muted); margin-top: 2px; line-height: 1.5; }
.setrow .val { font-size: var(--t-small); color: var(--muted); flex: none; }
.setrow .chev { color: var(--faint); display: inline-flex; flex: none; }
.setrow .ic { color: var(--muted); }
/* saved cards (My Page): masked rows + quiet delete */
.pm-empty { display: flex; gap: 12px; align-items: flex-start; }
.pm-empty .ic { color: var(--muted); margin-top: 1px; }
.pm-t { font-weight: 700; font-size: var(--t-body); }
.pm-del { flex: none; color: var(--err); font-size: var(--t-small); font-weight: 600; padding: 8px 10px; border-radius: 10px; }
.pm-del:disabled { opacity: .5; }
/* vehicle management (own screen): one block per car, quiet actions */
.veh-item { padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
.veh-item:last-child { border-bottom: none; }
.veh-item:first-child { padding-top: 2px; }
.veh-head { display: flex; align-items: flex-start; gap: var(--s3); }
.veh-head .ic { color: var(--muted); margin-top: 2px; }
.veh-head .grow { flex: 1; min-width: 0; }
.veh-head .vname { font-weight: 700; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.veh-head .sub { display: block; font-size: var(--t-meta); color: var(--muted); margin-top: 2px; line-height: 1.5; }
.veh-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.veh-btn { font-size: var(--t-small); font-weight: 600; padding: 7px 14px; border-radius: 10px; background: var(--surface2); color: var(--text); }
.veh-btn:disabled { opacity: .5; }
.veh-btn.danger { color: var(--err); }
.veh-btn.confirm { background: var(--err); color: #fff; }
button.setrow:active { transform: none; opacity: .65; }
.seg-mini { display: flex; gap: 2px; padding: 3px; background: var(--surface2); border-radius: 999px; flex: none; }
.seg-mini button { padding: 6px 12px; border-radius: 999px; font-size: var(--t-meta); font-weight: 600; color: var(--muted); }
.seg-mini button.on { background: var(--surface); color: var(--text); box-shadow: 0 1px 4px rgba(10,30,60,.18); }
.switch { position: relative; width: 48px; height: 28px; border-radius: 999px; background: var(--surface3); flex: none; transition: background-color .2s; }
.switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 50%; background: #fff; box-shadow: 0 1px 4px rgba(10,30,60,.3); transition: transform .2s var(--ease); }
.switch[aria-checked="true"] { background: var(--go); }
.switch[aria-checked="true"]::after { transform: translateX(20px); }
.saved-note { font-size: var(--t-small); color: var(--go); font-weight: 600; min-height: 18px; margin-top: var(--s2); }
/* My Page settings — partial neumorphic tint (2026-10-27): settings cards
   become soft plates, each row a gently raised pill on the recessed ground.
   Toggle ON/OFF stays explicit (green fill + knob travel + state text);
   accent stays orange-only on primary actions. Scoped to My Page only. */
#screenMyPage .card.neu-card {
  background: linear-gradient(180deg, #f2f3f5, #eceef0);
  border-color: rgba(255, 255, 255, .7);
  box-shadow: 6px 8px 20px rgba(163, 177, 198, .28), -6px -6px 16px rgba(255, 255, 255, .8), inset 0 1px 0 rgba(255, 255, 255, .9);
  padding: 10px;
}
#screenMyPage .neu-card .setrow {
  border-bottom: none; margin: 6px 0; padding: 12px;
  background: #f8f9fa; border-radius: 16px;
  box-shadow: 3px 3px 8px rgba(163, 177, 198, .30), -3px -3px 8px rgba(255, 255, 255, .95);
}
#screenMyPage .neu-card .setrow:first-child { margin-top: 2px; }
#screenMyPage .neu-card .setrow:last-child { margin-bottom: 2px; }
#screenMyPage .neu-card .seg-mini { background: #e6e8eb; box-shadow: inset 2px 2px 5px rgba(163, 177, 198, .45), inset -2px -2px 5px rgba(255, 255, 255, .9); }
#screenMyPage .neu-card .seg-mini button.on { box-shadow: 2px 2px 5px rgba(163, 177, 198, .35), -1px -1px 3px rgba(255, 255, 255, .9); }
#screenMyPage .neu-card .switch { background: #dde0e4; box-shadow: inset 2px 2px 5px rgba(163, 177, 198, .50), inset -2px -2px 5px rgba(255, 255, 255, .85); }
#screenMyPage .neu-card .switch[aria-checked="true"] { background: var(--go); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .18), 0 1px 2px rgba(10, 30, 60, .12); }
#screenMyPage .neu-card .switch::after { box-shadow: 0 1px 4px rgba(10, 30, 60, .35), inset 0 0 0 1px rgba(10, 30, 60, .04); }
.notif-row { display: flex; gap: var(--s3); padding: 12px 2px; border-bottom: 1px solid var(--line-soft); width: 100%; text-align: left; }
.notif-row:last-child { border-bottom: none; }
.notif-row .ndot { width: 8px; height: 8px; border-radius: 50%; background: var(--brand); flex: none; margin-top: 7px; }
.notif-row .ndot.read { background: var(--surface3); }
.notif-row .nt { font-size: 14px; font-weight: 700; line-height: 1.45; }
.notif-row .nb { font-size: var(--t-small); color: var(--muted); line-height: 1.55; margin-top: 1px; }
.notif-row .ntime { font-size: var(--t-label); color: var(--faint); margin-top: 3px; }
button.notif-row:active { transform: none; opacity: .65; }
details.legal { border-bottom: 1px solid var(--line-soft); }
details.legal:last-child { border-bottom: none; }
details.legal summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: var(--s3);
  padding: 14px 2px; font-size: var(--t-body); font-weight: 600;
}
details.legal summary::-webkit-details-marker { display: none; }
details.legal summary .chev { margin-left: auto; color: var(--faint); display: inline-flex; transition: transform .25s var(--ease); }
details.legal[open ] summary .chev { transform: rotate(90deg); }
details.legal .lbody { font-size: var(--t-small); color: var(--muted); line-height: 1.8; padding: 0 2px 14px; }
details.legal .lbody b { color: var(--text); }

/* ---------- worker contract application ---------- */
.form-title { font-size: var(--t-title); font-weight: 800; margin-bottom: 4px; }
.photo-pick { display: flex; align-items: center; gap: var(--s3); }
.file-hidden { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.file-btn {
  display: inline-flex; align-items: center; margin: 0; padding: 10px 14px;
  font-size: var(--t-small); font-weight: 700; color: var(--text);
  background: var(--surface2); border: 1px solid var(--line); border-radius: var(--r-btn); cursor: pointer;
}
.file-btn:active { background: var(--surface3); }
.file-btn.main { background: var(--text); color: #fff; border-color: var(--text); }
.file-btn.main:active { background: #000; }
.photo-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: 10px; border: 1px solid var(--line); flex: none; }

/* ---------- eKYC-style guided capture (worker application, 2026-10-29) ----
   One step at a time: a framed capture area with corner marks (an oval for
   the face), quiet guidance, camera-first actions. Monochrome throughout. */
.ekyc-dots { display: flex; gap: 6px; margin: 12px 0 4px; flex-wrap: wrap; }
.ekyc-dot {
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  border-radius: 999px; padding: 6px 12px; font-size: var(--t-label); font-weight: 700; cursor: pointer;
}
.ekyc-dot.on { background: var(--text); border-color: var(--text); color: #fff; }
.ekyc-dot.done { border-color: var(--text); color: var(--text); }
.ekyc-dot.done.on { color: #fff; }
.ekyc-panel { margin-top: 10px; }
.ekyc-frame {
  position: relative; border-radius: 16px; overflow: hidden;
  background: var(--surface2); border: 1px solid var(--line);
  display: grid; place-items: center; margin-bottom: 10px;
}
.ekyc-frame.lic { aspect-ratio: 8 / 5; }
.ekyc-frame.edge { aspect-ratio: 4 / 3; }
.ekyc-frame.face { aspect-ratio: 4 / 4.4; }
.ekyc-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ekyc-hint {
  position: relative; z-index: 2; text-align: center; font-size: var(--t-small);
  color: var(--muted); line-height: 1.7; padding: 16px;
}
.ekyc-hint strong { color: var(--text); }
.ek-corner { position: absolute; width: 22px; height: 22px; z-index: 3; pointer-events: none; }
.ek-corner.tl { top: 10px; left: 10px; border-top: 3px solid var(--text); border-left: 3px solid var(--text); border-top-left-radius: 6px; }
.ek-corner.tr { top: 10px; right: 10px; border-top: 3px solid var(--text); border-right: 3px solid var(--text); border-top-right-radius: 6px; }
.ek-corner.bl { bottom: 10px; left: 10px; border-bottom: 3px solid var(--text); border-left: 3px solid var(--text); border-bottom-left-radius: 6px; }
.ek-corner.br { bottom: 10px; right: 10px; border-bottom: 3px solid var(--text); border-right: 3px solid var(--text); border-bottom-right-radius: 6px; }
.ekyc-oval {
  position: absolute; z-index: 2; width: 46%; aspect-ratio: 3 / 4;
  top: 42%; left: 50%; transform: translate(-50%, -50%);
  border: 2px dashed rgba(10, 30, 60, .45); border-radius: 50%; pointer-events: none;
}
.ekyc-frame.face .ekyc-hint { position: absolute; z-index: 3; bottom: 4px; left: 0; right: 0; }
.ekyc-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.ekyc-nav { display: flex; justify-content: space-between; align-items: center; margin-top: 14px; gap: 10px; }
.ekyc-nav .btn-ghost, .ekyc-nav .btn-primary { width: auto; padding-left: 22px; padding-right: 22px; }

/* ---------- licence guided camera (worker application, 2026-10-29) ----
   In-app camera for the licence front/back steps: a live view with a
   card frame guide (corner marks, everything outside the card dimmed),
   a still-confirm step, and plain quality cautions. The stage is dark
   like every bank eKYC camera; the app around it stays monochrome. */
.cam-overlay {
  position: fixed; inset: 0; z-index: 200; background: #0b0b0d; color: #fff;
  display: flex; flex-direction: column; padding: 14px 16px calc(16px + env(safe-area-inset-bottom));
  overscroll-behavior: contain;
}
.cam-head { position: relative; text-align: center; padding: 6px 44px 10px; }
.cam-title { font-size: var(--t-title); font-weight: 800; }
.cam-step { font-size: var(--t-label); color: rgba(255,255,255,.62); margin-top: 3px; font-weight: 700; letter-spacing: .04em; }
.cam-x {
  position: absolute; top: 0; right: 0; width: 40px; height: 40px;
  font-size: 24px; line-height: 1; color: #fff; border-radius: 50%;
  background: rgba(255,255,255,.12);
}
.cam-stage {
  position: relative; flex: 1; min-height: 0; border-radius: 16px; overflow: hidden;
  background: #000; display: grid; place-items: center;
}
.cam-stage video, .cam-stage img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.cam-guide {
  position: relative; z-index: 2; width: 86%; max-width: 430px; aspect-ratio: 85.6 / 54;
  border-radius: 10px; box-shadow: 0 0 0 9999px rgba(4,4,6,.58); pointer-events: none;
}
.cam-corner { position: absolute; width: 26px; height: 26px; }
.cam-corner.tl { top: -3px; left: -3px; border-top: 4px solid #fff; border-left: 4px solid #fff; border-top-left-radius: 10px; }
.cam-corner.tr { top: -3px; right: -3px; border-top: 4px solid #fff; border-right: 4px solid #fff; border-top-right-radius: 10px; }
.cam-corner.bl { bottom: -3px; left: -3px; border-bottom: 4px solid #fff; border-left: 4px solid #fff; border-bottom-left-radius: 10px; }
.cam-corner.br { bottom: -3px; right: -3px; border-bottom: 4px solid #fff; border-right: 4px solid #fff; border-bottom-right-radius: 10px; }
.cam-guide-text { text-align: center; font-weight: 700; font-size: var(--t-body); margin-top: 14px; }
.cam-tips { text-align: center; font-size: var(--t-label); line-height: 1.75; color: rgba(255,255,255,.68); margin-top: 8px; }
.cam-overlay .cam-err { text-align: center; margin-top: 8px; color: #ff9d8a; }
.cam-overlay .cam-err:empty { display: none; }
.cam-bar { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 14px; min-height: 64px; }
.cam-shutter {
  width: 64px; height: 64px; border-radius: 50%; flex: none;
  background: #fff; border: 4px solid rgba(255,255,255,.35);
  box-shadow: 0 0 0 2px rgba(0,0,0,.35);
}
.cam-shutter:active { transform: scale(.94); }
.cam-shutter:disabled { opacity: .45; }
.cam-overlay .cam-btn { width: auto; flex: 1; max-width: 240px; margin-top: 0; border-radius: 999px; }
.cam-overlay .btn-primary.cam-btn { background: #fff; color: #0f2140; box-shadow: none; }
.cam-overlay .btn-ghost.cam-btn { background: transparent; color: #fff; border-color: rgba(255,255,255,.45); }

/* ---------- worker preview mode (registered / pending, 2026-10-29) ---- */
.preview-note { border: 1px dashed var(--line); background: var(--surface2); font-size: var(--t-small); line-height: 1.75; color: var(--text); }

/* ---------- customer guest preview bar (2026-10-29) ---- */
.guest-bar {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface2); border: 1px solid var(--line-soft);
  border-radius: 14px; padding: 10px 12px; margin-bottom: 12px;
}
.guest-bar-text { flex: 1; min-width: 0; font-size: var(--t-small); font-weight: 700; color: var(--text); line-height: 1.5; }
.guest-bar-text span { display: block; font-weight: 500; color: var(--muted); font-size: var(--t-label); }
.guest-bar-btn {
  flex: none; background: var(--text); color: #fff; border: 1px solid var(--text);
  border-radius: 999px; padding: 9px 16px; font-size: var(--t-small); font-weight: 700; cursor: pointer;
}
.guest-bar-btn:active { background: #000; }
.terms-box {
  max-height: 240px; overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: var(--surface2); border: 1px solid var(--line-soft); border-radius: var(--r-input);
  padding: 12px 14px; font-size: var(--t-small); line-height: 1.75; color: var(--text);
}
.terms-box p + p { margin-top: 8px; }
.terms-box ol { margin: 8px 0 8px 18px; display: grid; gap: 6px; }
.agree-row {
  display: flex; align-items: flex-start; gap: 10px; margin-top: var(--s4);
  font-size: var(--t-small); color: var(--text); line-height: 1.6; cursor: pointer;
}
.agree-row input { width: 20px; height: 20px; flex: none; margin-top: 1px; accent-color: var(--brand); }
/* admin: identity-photo comparison */
.photo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.photo-grid figure { margin: 0; }
.photo-grid figcaption { font-size: var(--t-label); color: var(--muted); margin-bottom: 4px; font-weight: 700; }
.photo-grid img { width: 100%; border-radius: 10px; border: 1px solid var(--line); display: block; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ================================================================
   WARM UI (2026-10-28) — the two owner reels, one language:
   Parcel = entrance grammar (white sheet, pill fields, signing-in
   splash); Voltra = calm inside (your car as hero, big numbers,
   dotted flow, map as hero with floating UI, fixed total bar).
   MONO PASS (2026-10-28, owner call): the entrances — both gates,
   splashes, the worker header — are monochrome now (white / off-white
   / ink, brand-EC discipline) and the mascot is retired from every
   entrance and from the whole worker side. Interior warm accents
   remain as they were. The MENISCUS dock and /admin are untouched.
   ================================================================ */

/* pill primary actions everywhere in the app (Aguante's liquid fill
   inherits the radius, so hold-to-confirm reads as one warm pill) */
.screen .btn-primary, .page-wrap .btn-primary { border-radius: 999px; }

/* ---------- splash (boot + signing in): the mark becomes the line ----------
   2026-10-29, fourth pass (owner, from his own screen recording of
   78961707): three corrections.
   1) THE RING THAT TURNS IS THE LOGO'S OWN. The orange comet that
      circled the mark is gone. The mark is now built from the real
      icon's own pixels — a ring-only cutout (images/ring-*.png,
      extracted from each surface's icon) over that icon's own
      ground colour — and the logo's broken white / ink ring turns
      slowly in place while the app loads. The diagonal channel
      through the ring makes the turn read clearly; at 3.2s per
      revolution, stately, it never reads as a spinner.
   2) THE ORANGE LINE GAUGE STAYS, RE-CHOREOGRAPHED into one
      continuous motion: swell → the ring turning (the loading
      state) → the mark settles into the line → the orange fill
      runs to 100% AT THE CENTRE → only then RMBoot lifts the
      completed line to the top and lands it as the RMProgress bar
      (RMBoot still waits for the fill, so the flight never cuts
      the show short). Orange belongs to this launch face only —
      the bloom and the fill; the app inside stays monochrome.
   3) NO LAYOUT ANIMATION ANYWHERE. The third pass morphed by
      transitioning the mark's width / height / border-radius and
      collapsing the wordmark's height — on device those stepped
      (the owner's recording). Now every moving property is
      transform or opacity: the mark squashes with scaleY and
      cross-dissolves into a separate fixed-geometry line at the
      same centre; the wordmark only fades; the background bloom
      no longer scales (a full-screen gradient repaint every
      frame) — it only fades in at a fixed size.
   2026-10-29, fifth pass (owner, on the fourth pass): the flight
   up read as an afterthought ("ゲージが上に行くのがなんかな");
   what he wants is the streaming-intro grammar — the mark
   ARRIVES from depth, small and far, spreading toward the viewer
   with its light opening around it, and that arrival is the
   star of the launch. So:
   4) THE ENTRANCE IS A DEPTH EMERGENCE. The lockup no longer
      swells up from below: it starts deep (scale .32, invisible)
      and comes forward over 1.1s on one long ease (bsEmerge),
      while a halo of the launch orange — local to the stage, so
      spreading it costs no full-screen repaint — opens out from
      behind the mark (bsHalo) and settles into the quiet glow
      the ring then turns inside. The ring, the squash into the
      line and the orange fill are unchanged from the fourth
      pass; only their cue times move later, behind the arrival.
   5) THE FLIGHT EARNS ITS PLACE. The completed line no longer
      hops through a dark pill on its way up (that stopover was
      the arbitrary beat the owner felt): it rises, widens and
      thins in ONE continuous glide — the centre spread, lifted
      to the top edge — its orange glow thinning with it and the
      colour handed to ink only at the landing (RMBoot,
      topbar-progress.js).
   2026-10-29, eighth pass (owner, on the fifth): the finale
   itself was the problem \u2014 he pointed at a streaming-style
   boot (YOUTRUST): white ground, the mark, the logotype
   quietly under it, done. No gauge, no flight up. So:
   6) THE FINALE IS THE LOCKUP, HELD. The morph into the line,
      the orange fill and the flight to the top are all retired
      (.bs-line / .bs-fill are gone, and with them the top-edge
      destination the owner kept bumping into). What loads is
      what lands: the mark emerges from depth, its own ring
      turns while the app loads, the logotype (and the role
      label, on the role surfaces) sits quietly under it \u2014 and
      the splash simply holds that composition until the content
      is ready. RMBoot then keeps one quiet beat on the lockup
      and fades the whole splash into the app. The surfaces'
      minimum display is 2000ms: long enough for the arrival to
      settle and the lockup to read, nothing more. The black
      gap before first paint is covered on iOS by
      apple-touch-startup-image (white, the app's own icon), so
      the launch reads on-brand from the first frame the OS
      shows.
   (History: pulse \u2192 morph+line \u2192 centre-complete \u2192 ring turns +
   startup image \u2192 depth emergence \u2192 per-surface role label \u2192
   logotype lockup \u2192 quiet lockup finale, see PROGRESS.) */
.boot-splash {
  --bs-orange: #ee5e01;
  position: absolute; inset: 0; z-index: 1200;
  background: linear-gradient(180deg, #ffffff 0%, #f2f2f0 100%);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  transition: opacity .45s var(--ease), visibility .45s var(--ease);
}
/* presence (owner, second pass): the orange bloom gives the plain
   splash depth. Fourth pass: fixed size, opacity only — scaling
   this full-screen radial gradient repainted it every frame. */
.boot-splash::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(58% 46% at 50% 42%, rgba(238, 94, 1, .20), rgba(238, 94, 1, 0) 70%);
  transform: scale(1.06);
  animation: bsBg 1.6s ease-out both;
}
@keyframes bsBg { from { opacity: 0; } to { opacity: 1; } }
.bs-lockup, .bs-text, .bs-retry { position: relative; }
.boot-splash.fixed { position: fixed; }
.boot-splash.hide { opacity: 0; visibility: hidden; pointer-events: none; }
.bs-lockup { display: flex; flex-direction: column; align-items: center; animation: bsEmerge 1.1s cubic-bezier(.16, .68, .16, 1) both; }
/* the stage: the mark's own square (eighth pass: the line that
   used to share this centre is retired, so the stage is the
   mark alone, centred over the logotype). */
.bs-stage { position: relative; width: 92px; height: 92px; flex: none; }
/* the light that opens around the arriving mark (fifth pass):
   a stage-local radial glow, spread with transform only — the
   full-screen bloom above stays a plain fade. It settles at the
   glow the turning ring then sits inside. */
.bs-halo {
  position: absolute; left: 50%; top: 50%; width: 340px; height: 340px;
  border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, rgba(238, 94, 1, .32) 0%, rgba(238, 94, 1, .12) 42%, rgba(238, 94, 1, 0) 68%);
  opacity: 0; transform: translate(-50%, -50%) scale(.22);
  animation: bsHalo 2s cubic-bezier(.16, .68, .16, 1) both;
  will-change: transform, opacity;
}
@keyframes bsHalo {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(.22); }
  40% { opacity: .95; }
  100% { opacity: .5; transform: translate(-50%, -50%) scale(1.14); }
}
.bs-mark {
  position: absolute; left: 50%; top: 50%;
  width: 92px; height: 92px; border-radius: 26px; overflow: hidden;
  border: 1px solid var(--line-soft);
  box-shadow: 0 14px 30px rgba(10, 30, 60, .14);
  transform: translate(-50%, -50%) scaleY(1);
  transition: transform .5s var(--ease), opacity .18s ease;
  will-change: transform;
}
/* the face carries each surface icon's own ground colour (scoped
   by splash id, exactly like the icons themselves differ) */
.bs-face { position: absolute; inset: 0; display: block; transition: opacity .25s ease; }
#bootSplash .bs-face { background: #000000; }
#wSplash .bs-face, #sSplash .bs-face { background: #dbd7d0; }
#aSplash .bs-face { background: #26262c; }
/* the logo's own broken ring, cut out of the real icon. It turns
   in place while the app loads (owner, fourth pass) — the orange
   comet that used to circle the mark is gone. */
.bs-ring {
  display: block; width: 100%; height: 100%;
  animation: bsSpin 3.2s linear infinite;
  will-change: transform;
}
@keyframes bsSpin { to { transform: rotate(360deg); } }
/* (eighth pass) the line the mark used to dissolve into — and
   its orange fill gauge — are retired: the finale is the
   lockup itself now, so there is nothing to dissolve into and
   nothing for a gauge to fill. The in-app top progress line
   (RMProgress, topbar-progress.js) is a separate feature and
   was never this element. */
/* the logotype (owner, seventh pass): not a text label but the
   brand lockup itself, set in the same grammar as the welcome
   gate's wordmark — REPAIRMAN in heavy caps with deliberate
   tracking, the katakana name riding small and grey at its side.
   It exists only while loading: under .error it simply stays
   put, and the splash's own fade takes it away at the end. On the
   role surfaces the lockup stays the senior line and .bs-role
   remains the small grey supplement under it. */
.bs-name { display: flex; align-items: baseline; justify-content: center; gap: 6px; margin-top: 18px; height: 24px; animation: bsFade 1s ease .55s both; transition: opacity .45s ease, transform .45s ease; }
.bs-wm { font-size: 19px; font-weight: 800; letter-spacing: .14em; line-height: 1; color: var(--text); white-space: nowrap; }
.bs-wm-jp { font-size: 10.5px; font-weight: 600; letter-spacing: .24em; line-height: 1; color: var(--muted); white-space: nowrap; }
/* the surface's role, said quietly under the wordmark (owner,
   sixth pass): the partner, partner-shop and admin splashes
   carry one small grey label so the loading screen itself says
   which surface this is. The customer splash keeps the plain
   lockup — no role word there. The label arrives a breath after
   the name and leaves with it; it is a label, not loading copy,
   so the no-loading-words rule above is untouched. */
.bs-role { font-size: 11px; font-weight: 600; letter-spacing: .24em; text-indent: .24em; color: var(--muted); margin-top: 7px; animation: bsFade 1s ease .8s both; transition: opacity .45s ease, transform .45s ease; }
/* no literal loading words (owner 2026-10-29): the element stays in
   the DOM because the failure path writes into it, but it renders
   only under .error — during a normal load the motion speaks. */
.bs-text { font-size: 13px; font-weight: 600; color: var(--muted); opacity: 0; transition: opacity .3s ease; }
.boot-splash.error .bs-text { opacity: 1; }
/* the arrival (fifth pass): from depth — small, far, invisible —
   the lockup comes forward on one long ease and is simply THERE
   at full size; no rise, no overshoot, no bounce. Opacity leads
   so the mark reads as materialising out of its own light. */
@keyframes bsEmerge {
  0% { opacity: 0; transform: scale(.32); }
  45% { opacity: 1; }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes bsFade { from { opacity: 0; } to { opacity: 1; } }
/* load failure: the show unwinds to the plain mark (the ring back
   at its resting angle), plain words and a way back */
.boot-splash.error .bs-mark { transform: translate(-50%, -50%) scaleY(1); opacity: 1; transition: none; }
.boot-splash.error .bs-face { opacity: 1; transition: none; }
.boot-splash.error .bs-ring { animation: none; transform: none; }
.boot-splash.error .bs-halo { animation: none; opacity: .5; transform: translate(-50%, -50%) scale(1.14); }
.boot-splash.error .bs-name { animation: none; opacity: 1; transform: none; }
.boot-splash.error .bs-role { animation: none; opacity: 1; transform: none; }
.bs-retry {
  margin-top: 14px; padding: 11px 26px; border-radius: 999px; border: 0;
  background: var(--accent); color: #fff; font-size: 14px; font-weight: 700; cursor: pointer;
  animation: bsFade .6s ease both;
}
.bs-retry:active { opacity: .85; }

/* ---------- customer gate: mono welcome (logo lockup + quiet type) ---------- */
#screenAuth .scroll { padding: calc(env(safe-area-inset-top, 0px) + 16px) 18px 44px; }
.welcome-hero { position: relative; padding: 16px 6px 0; }
.wh-lockup { display: flex; align-items: center; gap: 11px; }
.wh-mark {
  width: 44px; height: 44px; border-radius: 12px; flex: none;
  box-shadow: 0 2px 10px rgba(10, 30, 60, .18), inset 0 0 0 1px rgba(10, 30, 60, .05);
}
.wh-words { display: inline-flex; align-items: center; gap: 9px; white-space: nowrap; }
.wh-wm { font-size: 17px; font-weight: 800; letter-spacing: .045em; line-height: 1; color: var(--text); }
.wh-wm-jp { font-size: 10px; font-weight: 600; letter-spacing: .24em; line-height: 1; color: var(--muted); }
.wh-title { margin-top: 30px; font-size: 29px; font-weight: 800; letter-spacing: .02em; color: var(--text); }
.wh-sub { margin-top: 8px; font-size: 13px; line-height: 1.65; color: var(--muted); }
.wh-chip {
  margin-top: 16px; display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; border-radius: 999px; background: #fff;
  border: 1px solid var(--line-soft);
  color: var(--muted); font-size: 11.5px; font-weight: 700;
  box-shadow: 0 4px 12px rgba(10, 30, 60, .06);
}
.wh-chip .ic { display: inline-flex; }
/* gate entrance: the lockup, the greeting and the chip settle in with a
   small stagger as the gate opens (same rise as the form's own) */
#screenAuth.open .wh-lockup { animation: auth-rise .45s var(--ease) both; }
#screenAuth.open .wh-copy { animation: auth-rise .45s var(--ease) .05s both; }
#screenAuth.open .wh-chip { animation: auth-rise .45s var(--ease) .1s both; }
#screenAuth .auth-wrap {
  margin-top: 18px; padding: 20px 22px 24px;
  /* frosted glass over the gate's world: translucent white, a real
     backdrop blur (the card never drags, so the 2026-11-01 sheet
     ghosting lesson does not apply), a bright rim and a deep soft
     shadow. Without backdrop-filter support it falls back to a
     near-opaque white so text contrast never depends on the blur. */
  background: rgba(255, 255, 255, .68);
  -webkit-backdrop-filter: blur(18px) saturate(1.5);
  backdrop-filter: blur(18px) saturate(1.5);
  border: 1px solid rgba(255, 255, 255, .7); border-radius: 28px;
  box-shadow: 0 24px 48px rgba(10, 30, 60, .14), 0 2px 8px rgba(10, 30, 60, .05), inset 0 1px 0 rgba(255, 255, 255, .85);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  #screenAuth .auth-wrap { background: rgba(255, 255, 255, .93); }
}
#screenAuth .auth-seg { background: #e9e9e7; border-radius: 999px; }
#screenAuth .auth-seg button { border-radius: 999px; }
#screenAuth input {
  height: 54px; padding: 0 20px; border-radius: 999px;
  border: 1px solid #e3e1dc; background: #fafaf9; box-shadow: none;
}
#screenAuth .pass-wrap input { padding-right: 54px; }
#screenAuth input:focus { background: #fff; border-color: var(--text); box-shadow: 0 0 0 4px rgba(10, 30, 60, .10); }
#screenAuth .auth-cta {
  background: var(--text);
  box-shadow: 0 12px 24px rgba(10, 30, 60, .20); font-size: 16.5px;
}
#screenAuth .auth-cta:active { background: var(--accent-deep); }
/* gate primaries beyond the sheet (password reset, worker auth/reset):
   the same orange as every primary action (2026-11-04 motif) */
#screenResetMember .btn-primary, #wAuthBox .btn-primary, #wResetBox .btn-primary {
  background: var(--accent); box-shadow: 0 10px 22px rgba(255, 106, 0, .24);
}
#screenResetMember .btn-primary:active, #wAuthBox .btn-primary:active, #wResetBox .btn-primary:active { background: var(--accent-deep); }
/* forgot-password link: quiet, right-aligned, just under the password
   field (Parcel's "Forgot Password?" seat) — auth work owns the flow */
.auth-forgot {
  display: block; margin: 10px 2px 0 auto; width: fit-content;
  color: var(--text); font-size: 13px; font-weight: 700; text-decoration: none;
}
/* terms-agreement row: keep the auth work's checkbox a checkbox —
   #screenAuth input (1,0,1) would otherwise stretch it into a pill */
#screenAuth .agree-row { margin: 2px 2px 14px; font-size: 12.5px; color: var(--muted); }
#screenAuth .agree-row input {
  width: 20px; height: 20px; padding: 0; margin-top: 1px; flex: none;
  border-radius: 6px; box-shadow: none; accent-color: var(--text);
}
#screenAuth .agree-row a { color: var(--text); font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }

/* ---------- home: car hero + dotted flow ---------- */
#homeSheet { padding-bottom: calc(118px + env(safe-area-inset-bottom, 0px)); }
/* drag mode (2026-10-29 clip fix): the 118px band above was sized when the
   SHEET itself scrolled. In the drag structure the sheet is a flex column
   and only .sheet-scroll scrolls, so that padding landed OUTSIDE the scroll
   area — it ate 118px of viewport and showed as a dead white band under the
   content (owner's 10:35 shot: hero clipped, CTA half-cut, void below).
   The tab bar is in-flow under .main; only the MENISCUS bead rides ~34px
   proud into .main, so in drag mode the sheet keeps a normal bottom padding
   and the bead clearance moves INSIDE the scroll area (.sheet-scroll). */
#homeSheet.sheet-drag { padding-bottom: var(--s4); }
.sheet-hero {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 12px; padding: 14px 16px 12px;
  background: #f7f7f9;
  border: 1px solid var(--line-soft); border-radius: 18px;
  overflow: hidden;
}
.sheet-hero.tap { cursor: pointer; -webkit-tap-highlight-color: transparent; transition: transform .18s var(--ease), opacity .18s var(--ease); }
.sheet-hero.tap:active { transform: scale(.982); opacity: .88; }
@media (prefers-reduced-motion: reduce) { .sheet-hero.tap { transition: none; } .sheet-hero.tap:active { transform: none; opacity: 1; } }
#pfVehicleAnchor { scroll-margin-top: 10px; }
.sh-text { flex: 1; min-width: 0; }
.sh-greet { font-size: 12.5px; font-weight: 700; color: var(--muted); }
.sh-car { margin-top: 3px; display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px 8px; }
.sh-car-name { font-size: 24px; font-weight: 800; letter-spacing: .01em; color: var(--ink); line-height: 1.15; }
.sh-car-spec { font-size: 12px; color: var(--muted); }
/* the hero is text only (2026-11-02): the car art that used to sit
   beside the greeting was retired by owner instruction — the card
   speaks with its words (greeting + guidance) alone. */

/* ---- hero deck + banner slot (2026-11-02) ----
   The hero slot is a deck: the front card, and the next card peeking
   behind it (smaller, lower, dimmer). Advancing glides the back card
   to the front — slow, single easing, no bounce, no flashing, per
   the house motion law. Cards are white on the hero's light ground;
   the banner slot (shown when nothing is unregistered) is a slim
   ink strip. Swipe targets reveal their labels from behind the
   card: right = next, left = もう表示しない. */
.hero-deck { position: relative; margin-top: 5px; }
.hd-stack { position: relative; height: 92px; }
.hd-card {
  position: absolute; left: 0; right: 0; top: 0; height: 84px;
  box-sizing: border-box;
  display: flex; align-items: center; gap: 11px;
  padding: 10px 13px;
  background: #fff; border: none; border-radius: 16px;
  box-shadow: 0 8px 22px rgba(10,30,60,.10), 0 1px 4px rgba(10,30,60,.05);
  color: var(--text); text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform .65s cubic-bezier(.25,.7,.25,1), opacity .65s cubic-bezier(.25,.7,.25,1);
}
.hd-card.front { z-index: 2; transform: none; opacity: 1; touch-action: pan-y; cursor: pointer; }
.hd-card.back { z-index: 1; transform: scale(.96) translateY(8px); opacity: .45; pointer-events: none; }
.hd-card.back-new { opacity: 0; }
.hd-card.dragging { transition: none; }
.hd-card.leaving-auto { transform: translateY(-8px); opacity: 0; pointer-events: none; }
.hd-card.leaving-next { transform: translateX(56px); opacity: 0; pointer-events: none; }
.hd-card.leaving-dismiss { transform: translateX(-72px); opacity: 0; pointer-events: none; }
.hd-ic {
  flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%;
  background: #f0f0f3; color: var(--text);
  display: flex; align-items: center; justify-content: center;
}
/* the address card stands out (design pass 2): while the address is
   unregistered the card itself is the call to action — ink-filled,
   so it reads as the one thing to do, not as another notice row. */
.hd-card.hd-standout { background: var(--brand); color: #fff; box-shadow: 0 12px 28px rgba(10,30,60,.28); }
.hd-card.hd-standout .hd-title { color: #fff; }
.hd-card.hd-standout .hd-sub { color: rgba(255,255,255,.72); }
.hd-card.hd-standout .hd-ic { background: rgba(255,255,255,.16); color: #fff; }
.hd-actions { display: flex; gap: 8px; align-items: center; }
.hd-text { flex: 1; min-width: 0; }
.hd-title { display: block; font-size: 18px; font-weight: 800; letter-spacing: .01em; color: var(--text); line-height: 1.3; }
.hd-sub { display: block; margin-top: 2px; font-size: 12px; color: var(--muted); line-height: 1.35; }
.hd-under {
  position: absolute; top: 0; height: 84px; z-index: 0;
  display: flex; align-items: center;
  font-size: 11px; font-weight: 700; color: var(--muted);
  opacity: 0; pointer-events: none;
}
.hd-under-l { left: 4px; }
.hd-under-r { right: 4px; }
.hd-foot { display: flex; align-items: center; justify-content: space-between; min-height: 32px; margin-top: 1px; }
.hd-dots { display: flex; gap: 5px; padding: 6px 2px; }
.hd-dots i { display: block; width: 5px; height: 5px; border-radius: 50%; background: #d9d9df; }
.hd-dots i.on { background: var(--text); }
.hd-later {
  border: 1px solid var(--line); background: #fff; color: var(--muted);
  font-size: 12px; font-weight: 700; border-radius: 999px;
  padding: 7px 14px; min-height: 32px; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.hd-later:active { background: #f0f0f3; }

/* missing-items slideshow (2026-11-03): screenSetup presents each
   unregistered profile item as one card on a horizontal snap
   track — the member swipes sideways through them (the home hero
   deck's card grammar, given a full screen). Quiet motion only:
   native snap scrolling, no auto-advance, dots mark the position.
   The track bleeds to the screen edges so the next card peeks in;
   cards keep the standard surface/shadow of the app's cards. */
.setup-lead { border-color: var(--line); }
.setup-deck {
  margin: 14px -18px 0; padding: 4px 18px 10px;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain; scrollbar-width: none;
}
.setup-deck::-webkit-scrollbar { display: none; }
.setup-track { display: flex; gap: 12px; }
.setup-card {
  flex: 0 0 calc(100% - 30px); scroll-snap-align: start;
  background: var(--surface); border: 1px solid var(--line-soft);
  border-radius: 20px; padding: 20px 18px 16px;
  box-shadow: 0 10px 26px rgba(10, 30, 60, .08);
  display: flex; flex-direction: column;
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.setup-card.leaving { opacity: 0; transform: translateX(-24px); }
.setup-ic {
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--brand); color: #fff;
  display: grid; place-items: center; margin-bottom: 12px;
}
.setup-title { font-size: 17px; font-weight: 800; letter-spacing: .01em; }
.setup-sub { font-size: 13px; line-height: 1.65; color: var(--muted); margin-top: 8px; }
.setup-go { margin-top: 16px; width: 100%; }
.setup-actions { display: flex; gap: 8px; margin-top: 12px; }
.setup-actions button {
  flex: 1; min-height: 44px; border-radius: 999px; cursor: pointer;
  font-size: 13px; font-weight: 700; -webkit-tap-highlight-color: transparent;
}
.setup-later { border: 1px solid var(--line); background: #fff; color: var(--text); }
.setup-later:active { background: #f0f0f3; }
.setup-dismiss { border: 1px solid transparent; background: transparent; color: var(--muted); }
.setup-dismiss:active { background: #f0f0f3; }
.setup-dots { display: flex; gap: 5px; justify-content: center; padding: 4px 0 2px; }
.setup-dots i { display: block; width: 5px; height: 5px; border-radius: 50%; background: #d9d9df; transition: background .25s var(--ease); }
.setup-dots i.on { background: var(--text); }

/* cross-service notices (2026-11-02 rework): the banner slot sits
   at the very bottom of the sheet and speaks in the same grammar
   as the other notice cards — small label, title, chevron on a
   plain surface card. No ink block, no rotation: the cards simply
   stand in a quiet column where the sheet ends. */
.bn-list { display: flex; flex-direction: column; gap: var(--gap-card); }
.bn-card {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-card);
  padding: 13px 16px; box-shadow: var(--shadow-card);
  color: var(--text); text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.bn-card:active { background: #f4f4f6; }
.bn-text { flex: 1; min-width: 0; }
.bn-kicker { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .09em; color: var(--muted); }
.bn-title { display: block; margin-top: 2px; font-size: 15px; font-weight: 700; line-height: 1.35; }
.bn-chev { flex: 0 0 auto; color: var(--faint); display: flex; }
@media (prefers-reduced-motion: reduce) {
  .hd-card { transition: opacity .2s linear; }
  .hd-card.back { transform: none; }
  .hd-card.leaving-auto, .hd-card.leaving-next, .hd-card.leaving-dismiss { transform: none; }
}
.active-code { margin-top: 2px; font-size: 27px; font-weight: 800; letter-spacing: .14em; color: var(--ink); }
.flowline { display: flex; align-items: flex-start; margin: 12px 2px 2px; }
.flowline.card-flow { margin: 0 0 12px; padding: 16px 14px 12px; background: #fff; border: 1px solid var(--line); border-radius: 16px; }
.fl-step { display: flex; flex-direction: column; align-items: center; gap: 5px; width: 34px; flex: 0 0 auto; }
.fl-dot {
  width: 24px; height: 24px; border-radius: 50%; background: #ececef; color: #fff;
  display: flex; align-items: center; justify-content: center;
}
.fl-step.done .fl-dot { background: var(--brand); }
.fl-step.now .fl-dot {
  background: #fff; border: 2px solid var(--brand); color: var(--brand-deep);
  box-shadow: 0 4px 12px rgba(10, 30, 60, .18); transform: scale(1.38);
}
.fl-lb { font-size: 10px; font-weight: 600; white-space: nowrap; color: var(--muted); }
.fl-step.done .fl-lb, .fl-step.now .fl-lb { color: var(--ink); }
.fl-bar { flex: 1; height: 3px; margin-top: 11px; border-radius: 99px; background: var(--surface2); }
.fl-bar.done { background: var(--brand); }
.tile .glyph { background: var(--surface2); }

/* ---------- tracking: map hero, floating sheet, pay bar ---------- */
/* tracking rebuild (2026-10-31): the map is the full-screen stage;
   --tpad is the sheet boundary (set from JS by the split drag) and
   only positions the horizon fade + grab handle over the stage.
   The var lives on #screenTrack (2026-11-01): the fade inherits it
   through the map wrap, and the grab handle — a direct child of the
   screen since the reachability fix below — inherits the same live
   value, so handle, fade and stack padding can never drift apart. */
#screenTrack { --tpad: calc(env(safe-area-inset-top, 0px) + 372px); }
.track-map { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0; }
.track-map #trackMap { position: absolute; inset: 0; }
.track-map::after {
  content: ""; position: absolute; left: 0; right: 0; top: calc(var(--tpad) - 92px); height: 92px;
  background: linear-gradient(180deg, rgba(244, 244, 246, 0), var(--bg)); pointer-events: none;
}
#screenTrack .head { position: relative; z-index: 2; }
#screenTrack #trackBody { position: relative; z-index: 1; background: transparent; }
/* .track-hero / .th-* retired 2026-11-02: the tracking hero is
   the boarding-pass (.pass) now; these rules had no references. */
.pay-bar {
  position: sticky; bottom: 10px; margin-top: 4px;
  padding: 12px 14px 10px; background: var(--surface);
  border: 1px solid var(--line-soft); border-radius: 18px;
  box-shadow: 0 10px 26px rgba(10, 30, 60, .13);
}
.pay-total { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 10px; }
.pay-total span { font-size: 12px; font-weight: 700; color: var(--muted); }
.pay-total b { font-size: var(--t-total); font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.pay-bar .pay-hint { margin-top: 8px; }
/* payment deadline row inside the pay bar: a quiet fact, not a warning */
.pay-due {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--t-meta); color: var(--muted); margin: -2px 0 10px;
}
.pay-due .ic { display: inline-flex; color: var(--faint); flex: none; }

/* ---------- unpaid gate card (home) ----------
   Amount-led and quiet: a plain heading says an earlier job is
   unpaid, a job line names which job (category / trouble / date),
   the amount is the hero; the request code, the 7-day deadline and
   the saved-card state are small fact rows; the bubble button is
   the payment path (re-register the card first when needed — the
   label says so) and a ghost button opens the job's detail.
   No warning prose. */
.gate-card { padding: 16px 18px 14px; border: none; box-shadow: 0 14px 34px rgba(10,30,60,.16), 0 2px 6px rgba(10,30,60,.06); }
.gate-due-line { margin-top: 10px; font-size: 15px; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; }
.gate-due-line .warn { color: var(--err); }
.gate-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.gate-label {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-meta); font-weight: 700; color: var(--muted); letter-spacing: .04em;
}
.gate-label .ic { display: inline-flex; }
.gate-chip {
  flex: none; font-size: 11px; font-weight: 700; padding: 4px 9px;
  border-radius: 999px; background: var(--surface2); color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.gate-chip.err { background: rgba(217, 45, 32, .08); color: var(--err); }
.gate-job {
  margin-top: 6px; font-size: var(--t-small); font-weight: 600;
  color: var(--text); line-height: 1.55;
}
.gate-detail { width: 100%; margin-top: 8px; }
.gate-amount {
  margin-top: 8px; font-size: 30px; font-weight: 800;
  letter-spacing: -.01em; font-variant-numeric: tabular-nums; color: var(--text);
}
.gate-rows { margin-top: 10px; border-top: 1px solid var(--line-soft); }
.gate-row {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 7px 0; font-size: var(--t-small); border-bottom: 1px solid var(--line-soft);
}
.gate-row span:first-child { color: var(--muted); flex: none; }
.gate-row span:last-child { font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }
.gate-row .gate-due-note { font-size: 10px; font-weight: 500; color: var(--sub); }
.gate-note { margin-top: 8px; font-size: var(--t-meta); color: var(--muted); line-height: 1.6; }
.gate-sub { margin-top: 8px; font-size: 11px; color: var(--faint); text-align: center; }
.btn-bubble.loading { pointer-events: none; opacity: .85; }

/* ---------- quote: fixed bottom total bar ---------- */
.quote-bar {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 18px calc(12px + env(safe-area-inset-bottom, 0px));
  background: rgba(255, 255, 255, .92); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--line);
}
.quote-bar .btn-primary { flex: 1; width: auto; margin-top: 0; }
.qb-total { display: flex; flex-direction: column; gap: 1px; flex: 0 0 auto; }
.qb-total span { font-size: 10.5px; font-weight: 700; color: var(--muted); }
.qb-total b { font-size: 26px; font-weight: 800; letter-spacing: .01em; }

/* ---------- worker head: mono welcome (partner mark + quiet type;
   the mascot is retired from the whole worker side, 2026-10-28) ---------- */
body:has(.page-wrap) { background: #f6f5f3; }
.warm-head {
  margin: -26px -18px 0; padding: 24px 22px 20px;
  background: #fff;
  border-radius: 0 0 30px 30px; text-align: left;
  border-bottom: 1px solid var(--line-soft);
  box-shadow: 0 10px 24px rgba(10, 30, 60, .07);
}
.warm-head .wh-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.warm-head .wh-left { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; }
.warm-head .appicon { margin: 0; }
.warm-head .lockup-words { justify-content: flex-start; }
.warm-head h1 { text-align: left; }
.warm-head .sub { margin-left: 0; margin-right: 0; text-align: left; color: var(--muted); }
#wAuthBox { margin-top: 18px; }
#wAuthBox.card, #wResetBox.card { border-color: var(--line-soft); border-radius: 24px; box-shadow: 0 14px 34px rgba(10, 30, 60, .08); }
#wAuthBox .auth-seg { background: #e9e9e7; border-radius: 999px; }
#wAuthBox .auth-seg button { border-radius: 999px; }
#wAuthBox input, #wResetBox input { height: 52px; padding: 0 20px; border-radius: 999px; background: #fafaf9; }
#wAuthBox .pass-wrap input, #wResetBox .pass-wrap input { padding-right: 54px; }
#wHome > .card:first-of-type { background: #fff; border-color: var(--line-soft); }

@media (prefers-reduced-motion: reduce) {
  .bs-lockup, .bs-ring, .bs-halo, .boot-splash::before, .bs-name, .bs-role, .bs-text, .bs-retry { animation: none !important; }
  .bs-halo { opacity: .5; transform: translate(-50%, -50%) scale(1.14); }
.bs-mark, .bs-face, .bs-name, .bs-role { transition: none !important; }
  .boot-splash { transition: none; }
}

/* ---------- category progress gauge (owner idea, 2026-10-28) ----------
   The category pictogram is the tank; liquid rises with status.
   Quiet motion only: a 1.5s rise on change, a slow wave drift. */
.gauge { display: block; }
.g-liquid { transition: transform 1.5s cubic-bezier(.22, .8, .24, 1); }
.g-wave { transform-box: fill-box; animation: gSlide 6.5s linear infinite; }
@keyframes gSlide { to { transform: translateX(-50%); } }
.active-gauge {
  display: flex; align-items: center; gap: 10px; margin-top: 10px;
  padding: 8px 12px; background: #f7f7f9;
  border: 1px solid var(--line-soft); border-radius: 14px;
  --gauge-detail: #ffffff;
}
.active-gauge .gauge-car { width: 54px; height: auto; }
.active-gauge .gauge:not(.gauge-car) { height: 30px; width: auto; }
.mini-nums { display: flex; align-items: baseline; gap: 8px; }
.mini-pct { font-size: 17px; font-weight: 800; }
.mini-pct small { font-size: 11px; font-weight: 700; }
.mini-status { font-size: 12px; font-weight: 600; color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
  .g-wave { animation: none !important; }
  .g-liquid { transition: none; }
}

/* worker head layout fix: the head is a centered flex column, so the
   band stretches instead of shrink-wrapping */
.page-head.warm-head { align-items: stretch; margin-top: 0; }

/* track pin: drops in once, then the halo breathes at a status pace —
   en_route/working breathe faster (alive), done rests green, quoted is
   still. The halo speaks about the request's state, never a position. */
.track-pin { animation: pinDrop .7s cubic-bezier(.22, .8, .24, 1) both; }
@keyframes pinDrop { from { opacity: 0; transform: translateY(-14px); } to { opacity: 1; transform: translateY(0); } }
.track-pin .halo { animation: haloBreathe 3.2s ease-in-out infinite; }
.track-pin[data-status="assigned"] .halo { animation-duration: 2.4s; }
.track-pin[data-status="en_route"] .halo,
.track-pin[data-status="arrived"] .halo,
.track-pin[data-status="working"] .halo { animation-duration: 1.5s; background: rgba(10, 30, 60, .16); border-color: rgba(10, 30, 60, .45); }
.track-pin[data-status="done"] .halo { animation: none; background: rgba(18, 164, 90, .14); border-color: rgba(18, 164, 90, .45); }
.track-pin[data-status="done"] .dotcore { background: #12a45a; }
/* live partner marker (実働 pass 2026-10-29): the moving partner, shown
   only while en route. A white target disc with an ink ring + centre
   dot and a small name tag — the inverse of the site's ink pin, so the
   two can never be confused. */
.partner-pin .halo { animation: haloBreathe 2s ease-in-out infinite; background: rgba(10,30,60,.05); border-color: rgba(10,30,60,.16); }
.partner-pin .dotcore { width: 18px; height: 18px; margin: -9px 0 0 -9px; background: #fff; border: 3px solid var(--brand); }
.partner-pin .dotcore::after { content: ""; position: absolute; left: 50%; top: 50%; width: 6px; height: 6px; margin: -3px 0 0 -3px; border-radius: 50%; background: var(--brand); }
.partner-pin .ptag { position: absolute; left: 50%; top: 33px; transform: translateX(-50%); background: var(--brand); color: #fff; font-size: 9px; font-weight: 700; letter-spacing: .04em; padding: 2px 6px; border-radius: 999px; white-space: nowrap; box-shadow: 0 2px 6px rgba(10,30,60,.25); }

/* ---------- tracking rebuild (2026-10-31): hero number + status
   line, partner card, searching rings, gliding marker states.
   Built from the shared tokens (--t-*, --surface, --shadow-card),
   no one-off palette. Quiet motion only — this is an emergency
   surface: no bounce, no overshoot, nothing playful. ---------- */
.tk-label { font-size: var(--t-label); font-weight: 700; letter-spacing: .14em; color: var(--muted); margin-top: 12px; }
.tk-minutes { font-size: 29px; font-weight: 700; line-height: 1.1; color: var(--text); font-variant-numeric: tabular-nums; }
.tk-minutes b { font-size: 62px; font-weight: 800; line-height: 1; margin: 0 3px; }
.tk-minutes small { font-size: 24px; font-weight: 800; }
.tk-big { margin-top: 12px; font-size: 29px; font-weight: 800; line-height: 1.25; color: var(--text); }
.tk-status { margin-top: 8px; font-size: var(--t-body); font-weight: 700; color: var(--text); }
.tk-note { margin-top: 4px; font-size: var(--t-meta); color: var(--faint); line-height: 1.5; }
.tk-steps { margin-top: 10px; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 5px; font-size: var(--t-meta); font-weight: 700; }
.tk-step { padding: 3px 9px; border-radius: 999px; border: 1px solid var(--line-soft); color: var(--faint); white-space: nowrap; }
.tk-step.done { color: var(--muted); background: var(--surface2); }
.tk-step.now { background: var(--text); border-color: var(--text); color: #fff; }
.tk-step-sep { color: var(--faint); font-weight: 400; }
.partner-card { display: flex; align-items: center; gap: 12px; }
.p-ava-wrap { position: relative; width: 48px; height: 48px; flex: none; }
.p-ava-fb { position: absolute; inset: 0; border-radius: 50%; background: var(--brand); color: #fff; display: grid; place-items: center; font-size: 19px; font-weight: 800; }
.p-ava { position: absolute; inset: 0; width: 48px; height: 48px; border-radius: 50%; object-fit: cover; border: 1px solid var(--line-soft); background: var(--surface2); }
.p-main { flex: 1; min-width: 0; }
.p-role { font-size: var(--t-label); font-weight: 700; letter-spacing: .1em; color: var(--muted); }
.partner-card .name { margin-top: 1px; }
/* searching beat: while no partner has accepted yet, two quiet rings
   widen around the site pin — the proximity search made visible.
   Nothing else moves; there are no fake partners to show. */
.track-pin .srings { display: none; }
.track-pin.searching .srings { display: block; }
.track-pin .srings i { position: absolute; left: 50%; top: 50%; width: 26px; height: 26px; margin: -13px 0 0 -13px; border-radius: 50%; border: 1.5px solid rgba(10,30,60,.45); opacity: 0; animation: sring 2.8s cubic-bezier(.22,.61,.36,1) infinite; }
.track-pin .srings i:nth-child(2) { animation-delay: 1.4s; }
@keyframes sring { 0% { transform: scale(.5); opacity: .55; } 70% { opacity: .16; } 100% { transform: scale(2.7); opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .track-pin .srings i { animation: none; }
}
/* gliding partner marker: the disc carries a direction arrow that
   the glide engine rotates toward travel direction */
.partner-pin .dotcore { display: grid; place-items: center; }
.partner-pin .dotcore::after { display: none; }
.partner-pin .prot { display: grid; place-items: center; line-height: 0; }
.partner-pin .prot svg { will-change: transform; }
/* stale stream: the partner's presence greys out and rests — the
   marker stays at the last real position and says so */
.partner-pin.stale .halo { animation: none; background: rgba(10,30,60,.04); border-color: rgba(10,30,60,.10); }
.partner-pin.stale .dotcore { border-color: #a7adb8; }
.partner-pin.stale .prot svg path { fill: #a7adb8; }
.partner-pin.stale .ptag { background: #a7adb8; }
@keyframes haloBreathe { 0%, 100% { transform: scale(1); opacity: .75; } 50% { transform: scale(1.22); opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .track-pin { animation: none; }
  .track-pin .halo { animation: none; }
}

/* ================================================================
   MOTION PASS (2026-10-28, owner call: "too web-like — make it glide
   like an app"). One quiet language everywhere: small travel, the
   house --ease, no bounce, no overshoot. Screen exits live with the
   .screen rules above; this section carries the rest.
   ================================================================ */

/* list entrances: children settle in with a short stagger; replays
   whenever a renderer rebuilds the list (fresh nodes re-animate) */
.list-in > * { animation: item-in .38s var(--ease) both; }
.list-in > *:nth-child(2) { animation-delay: .045s; }
.list-in > *:nth-child(3) { animation-delay: .09s; }
.list-in > *:nth-child(4) { animation-delay: .135s; }
.list-in > *:nth-child(5) { animation-delay: .18s; }
.list-in > *:nth-child(6) { animation-delay: .225s; }
.list-in > *:nth-child(n+7) { animation-delay: .27s; }
@keyframes item-in { from { opacity: 0; transform: translateY(7px); } }

/* standalone-page views (worker / admin): a view fades-rises as it is
   unhidden instead of popping in — display:none -> block restarts the
   animation, so no JS hook is needed */
#wAuthBox:not(.hidden), #wResetBox:not(.hidden), #wContractBox:not(.hidden), #wHome:not(.hidden),
#consoleBox:not(.hidden),
#segRequests:not(.hidden), #segWorkers:not(.hidden), #segMembers:not(.hidden),
#segResets:not(.hidden), #segSettings:not(.hidden) {
  animation: box-in .32s var(--ease) both;
}
@keyframes box-in { from { opacity: 0; transform: translateY(6px); } }

@media (prefers-reduced-motion: reduce) {
  .list-in > *, #wAuthBox:not(.hidden), #wResetBox:not(.hidden),
  #wContractBox:not(.hidden), #wHome:not(.hidden), #consoleBox:not(.hidden),
  #segRequests:not(.hidden), #segWorkers:not(.hidden), #segMembers:not(.hidden),
  #segResets:not(.hidden), #segSettings:not(.hidden),
  .screen.closing { animation: none !important; }
}

/* ---------- draggable sheets (owner, 2026-10-28): the map is the hero —
   both map screens hand the map back on a drag.
   Home: the grab handle is fixed at the sheet's top while the content
   scrolls inside (.sheet-scroll); the sheet rides translateY between
   three snaps — expanded / normal (the previous look) / collapsed
   (the greeting card alone at the bottom edge, map nearly full).
   Track: a quiet grab dash ON the sheet boundary line trades map
   height against the info stack (re-presented 2026-11-01 — the
   white glass pill floated detached over the fade and read as a
   rendering artifact, and inside the z-0 map wrap the scroll body
   covered it, so the drag could never be grabbed at all).
   The handle is the first row of the sheet's OWN content (device
   feedback 2026-11-01, second pass): as a fixed overlay pinned to
   the seam it stayed mid-screen while the stack scrolled beneath
   it — the bar must belong to the sheet, so it now scrolls with
   the content and can never overlay it. Its negative top margin
   pulls the 44px hit zone up into the stack's top padding, so at
   rest the dash sits exactly on the boundary line (--tpad) and
   the first card keeps its composed offset below it.
   House ease, small travel, no bounce. */
.sheet.sheet-drag { max-height: none; display: flex; flex-direction: column; overflow: hidden; }
/* overflow-x: hidden — this scroller is vertical-only BY DESIGN.
   With overflow-x left at its computed auto, any child that ever
   overflows horizontally (the tile grid did, by 29px) turns the
   sheet into a two-axis scroller: content drifts sideways during
   vertical scrolls and can rest offset, clipping the heading
   (owner's device video, 2026-11-02). Hidden makes a horizontal
   scroll position impossible, whatever a child does. */
.sheet-scroll { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: none; -webkit-overflow-scrolling: touch; padding-bottom: 34px; }
.sheet .grab {
  width: 100%; height: 24px; margin: 0 auto 2px; flex: none;
  background: none; border-radius: 0; position: relative;
  cursor: grab; touch-action: none;
}
.sheet .grab::before {
  content: ""; position: absolute; left: 50%; top: 7px; transform: translateX(-50%);
  width: 38px; height: 4.5px; border-radius: 999px; background: var(--surface3);
}
.sheet .grab:active { cursor: grabbing; }
.track-grab {
  position: relative; width: 120px; height: 44px; margin: -44px auto 0;
  background: transparent; border: 0; box-shadow: none;
  cursor: grab; touch-action: none;
}
.track-grab span {
  position: absolute; left: 50%; bottom: 0; transform: translate(-50%, 50%);
  width: 38px; height: 4.5px; border-radius: 999px; background: var(--surface3); display: block;
}
.track-grab:active { cursor: grabbing; }

/* ---------- two-way ratings (2026-10-29) ----------
   PlainWalk grammar: the average is the hero number, its label stays
   small and gray. Ink only — a filled star is state, not decoration. */
.rate-avg { display: flex; align-items: baseline; gap: var(--s2); margin-top: 6px; }
.rate-avg .rnum { font-size: 30px; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums; display: inline-flex; align-items: center; gap: 5px; }
.rate-avg .rnum svg { color: var(--text); fill: currentColor; }
.rate-avg .rlab { font-size: var(--t-label); color: var(--muted); }
.rate-none { font-size: var(--t-meta); color: var(--muted); margin-top: 6px; }
.rate-stars { display: flex; gap: 8px; margin: 10px 0 4px; }
.rate-star {
  width: 46px; height: 46px; border-radius: 50%; flex: none;
  border: 1px solid var(--line); background: var(--surface);
  color: #c3c8d0; display: grid; place-items: center;
}
.rate-star.on { color: var(--text); border-color: var(--text); }
.rate-star.on svg { fill: currentColor; }
.rate-show { display: inline-flex; gap: 2px; color: #c3c8d0; vertical-align: -3px; }
.rate-show .on { color: var(--text); }
.rate-show .on svg { fill: currentColor; }
.rate-comment { margin-top: 10px; }
.rate-done-note { display: flex; align-items: center; gap: 8px; font-size: var(--t-small); font-weight: 700; margin-top: 4px; }

/* ---- worker standby home (approved partners, 2026-10-29) ----
   Map-first waiting screen. Monochrome base; colour appears only as
   state (the badge dot / .go .warn). While offline the map itself is
   desaturated — the state reads before any text does. */
body.rm-approved .page-head .sub { display: none; }
.wmap-wrap { position: relative; height: 46vh; min-height: 300px; max-height: 460px; border-radius: 18px; overflow: hidden; border: 1px solid var(--line-soft); margin-top: 14px; background: #efede8; }
.wmap-wrap #wMap { position: absolute; inset: 0; }
.wmap-wrap #wMap.dark { background: #0d1013; }
.wmap-wrap.off #wMap { filter: grayscale(.75) brightness(.97); }
.wmap-top { position: absolute; top: 10px; left: 10px; z-index: 500; pointer-events: none; }
.wmap-top .status-badge { background: rgba(255,255,255,.94); box-shadow: 0 1px 6px rgba(10,30,60,.18); }
.wmap-fallback { position: absolute; left: 10px; right: 10px; bottom: 10px; z-index: 500; background: rgba(255,255,255,.94); border-radius: 10px; padding: 8px 10px; }
.wmap-note { margin-top: 8px; }
.w-online-btn { width: 100%; margin-top: 12px; padding: 15px; font-size: 16px; min-height: 52px; touch-action: manipulation; }
.w-online-btn:disabled { opacity: .55; box-shadow: none; }
.w-online-state { margin-top: 8px; font-size: var(--t-meta); color: var(--muted); text-align: center; line-height: 1.55; }
.wactive { display: block; width: 100%; text-align: left; margin-top: 14px; cursor: pointer; }
.wcond { display: block; width: 100%; text-align: left; }
.w-dock .wcond { margin-top: 0; margin-bottom: 10px; }
/* the three stats share one row; anything else rendered into the
   box (the cancel-fee note) wraps to its own full-width row below
   them — as a 4th flex item it starved the stat columns to a few
   characters wide and the labels crushed vertically (device
   report 2026-11-04). */
.wstats { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
.wstats-note { flex: 1 1 100%; text-align: left; }
.wstat { flex: 1; background: var(--surface); border: 1px solid var(--line-soft); border-radius: 14px; padding: 10px 6px; text-align: center; box-shadow: var(--shadow-card); }
.wstat-num { font-size: 22px; font-weight: 800; line-height: 1.15; color: var(--text); }
.wstat-num.sm { font-size: 16px; line-height: 1.5; }
.wstat-lab { font-size: var(--t-label); color: var(--muted); margin-top: 2px; }
.wdemand { margin-top: 10px; font-size: var(--t-meta); color: var(--muted); text-align: center; line-height: 1.55; min-height: 1em; }
.wmenu { border: 1px solid var(--line-soft); border-radius: 14px; overflow: hidden; background: var(--surface); box-shadow: var(--shadow-card); }
.wmenu-row { display: flex; align-items: center; gap: 8px; width: 100%; padding: 15px 14px; background: none; border: none; border-bottom: 1px solid var(--line-soft); font-size: var(--t-body); font-weight: 600; color: var(--text); cursor: pointer; text-align: left; font-family: inherit; }
.wmenu-row:last-child { border-bottom: none; }
.wmenu-label { flex: none; }
.wmenu-meta { flex: 1; text-align: right; font-size: var(--t-meta); color: var(--muted); font-weight: 500; }
.wmenu-chev { color: var(--faint); font-size: 18px; line-height: 1; }
.wview-back { background: none; border: none; color: var(--text); font-size: var(--t-small); font-weight: 700; padding: 14px 0 2px; cursor: pointer; font-family: inherit; }

/* ---- worker standby home, stage form (2026-10-31 rebuild) ----
   The map is the hero, full-bleed; every control floats on it and
   the home itself does not scroll. State reads off the map (pill,
   demand banner), the single primary action is anchored in the
   bottom dock with the three key numbers, and the menu is its own
   screen. Smaller, quieter proportions throughout (design-scale
   law). Scoped to the standby stage / approved mode only — the
   application flow for unapproved partners is untouched. */
body.rm-approved .page-wrap { position: relative; }
body.rm-approved:has(#wStandby:not(.hidden)) .page-wrap { overflow-y: hidden; }
/* the shared box-in entrance animation leaves a filled identity
   transform on #wHome, which would make it the containing block
   for the stage's absolutely positioned layers and collapse the
   stage to #wHome's in-flow height. The approved home enters
   through the splash fade instead — no box-in there. */
body.rm-approved #wHome { animation: none; }
body.rm-approved .warm-head { position: relative; z-index: 700; background: transparent; border-bottom: none; box-shadow: none; border-radius: 0; padding-top: calc(env(safe-area-inset-top, 0px) + 16px); padding-bottom: 6px; }
body.rm-approved .warm-head .appicon { width: 30px; height: 30px; border-radius: 9px; }
body.rm-approved .warm-head h1 { font-size: 15px; margin-top: 5px; letter-spacing: .04em; }
body.rm-approved #wNotifyPromptMount { position: absolute; top: calc(env(safe-area-inset-top, 0px) + 208px); left: 14px; right: 14px; z-index: 800; }
body.rm-approved #wNotifyPromptMount:empty { display: none; }
/* the prompt belongs to the standby stage — never float it over a
   sub-view the partner opened from the menu */
body.rm-approved:has(.wview:not(.hidden)) #wNotifyPromptMount { display: none; }
#wStandby { position: absolute; inset: 0; z-index: 5; }
#wStandby .wmap-wrap { position: absolute; inset: 0; height: auto; min-height: 0; max-height: none; margin: 0; border: none; border-radius: 0; }
#wStandby .wmap-top { top: calc(env(safe-area-inset-top, 0px) + 106px); left: 12px; }
#wStandby .wmap-fallback { bottom: auto; top: calc(env(safe-area-inset-top, 0px) + 212px); }
.w-menu-btn { position: absolute; top: calc(env(safe-area-inset-top, 0px) + 106px); right: 12px; z-index: 600; background: rgba(255,255,255,.94); border: 1px solid var(--line-soft); box-shadow: 0 1px 6px rgba(10,30,60,.18); border-radius: 999px; padding: 7px 14px; min-height: 44px; display: inline-flex; align-items: center; font-size: 13px; font-weight: 700; color: var(--text); font-family: inherit; cursor: pointer; touch-action: manipulation; }
#wStandby .wdemand { position: absolute; top: calc(env(safe-area-inset-top, 0px) + 156px); left: 12px; right: 12px; z-index: 600; margin: 0; min-height: 0; text-align: left; background: rgba(255,255,255,.95); border: 1px solid var(--line-soft); border-radius: 12px; box-shadow: 0 4px 14px rgba(10,30,60,.14); padding: 10px 12px; font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.45; cursor: pointer; touch-action: manipulation; }
#wStandby .wdemand:empty { display: none; }
/* the dock is a layout band, not a surface (hit audit 2026-11-04):
   the band itself — and its display-only children (stats, state
   pill, error line) — let touches fall through to the map; only the
   elements that DO something take the tap. A tap that used to land
   on the invisible band between controls now reaches the map it
   was aimed at, and every control keeps its own finger-sized box. */
.w-dock { position: absolute; left: 12px; right: 12px; bottom: calc(env(safe-area-inset-bottom, 0px) + 12px); z-index: 600; display: flex; flex-direction: column; gap: 10px; pointer-events: none; }
.w-dock button, .w-dock .wactive, .w-dock .wcond { pointer-events: auto; }
.w-dock .wactive { margin-top: 0; }
.w-dock .wstats { margin-top: 0; background: rgba(255,255,255,.92); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border: 1px solid var(--line-soft); border-radius: 16px; padding: 10px 4px; box-shadow: 0 4px 14px rgba(10,30,60,.10); }
.w-dock .wstat { background: transparent; border: none; box-shadow: none; border-radius: 0; padding: 2px 4px; }
.w-dock .wstat-num { font-size: 24px; }
.w-dock .wstats-note { padding: 2px 8px 4px; }
.w-dock .w-online-state { margin: 0; align-self: center; background: rgba(255,255,255,.88); border-radius: 999px; padding: 4px 12px; font-size: 12px; }
.w-dock .w-route-note { margin: 0; align-self: center; background: rgba(255,255,255,.88); border-radius: 999px; padding: 4px 12px; font-size: 12px; color: var(--muted); }
.w-dock .w-online-state:empty { display: none; }
.w-dock .w-online-btn { margin-top: 0; box-shadow: 0 10px 24px rgba(10,30,60,.22); }
.w-dock .err-text { text-align: center; margin: 0; }
.w-dock .err-text:empty { display: none; }

/* ---- partner button pass (2026-11-01): ETA editor row, quiet
   sub-actions, demoted toggle during an active job ----
   The ETA editor is one labelled row everywhere it appears (job
   card, assignment sheet): caption + a minutes field with a FIXED
   comfortable width + unit. It never flex-fights the main action —
   the old shared row starved the input to a blank ~66px box that
   read as a broken ghost element on the partner's phone. */
/* auto ETA (2026-11-01): the estimate is computed from the live
   route — surfaces show it as a quiet read-only line, never an
   input. .eta-edit survives only for the part name/cost rows. */
.eta-auto-line {
  display: flex; align-items: center; gap: 7px; margin-top: 12px;
  font-size: var(--t-small); color: var(--muted); line-height: 1.55;
}
.eta-auto-line b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }
.jobsheet-map {
  height: 176px; border-radius: 14px; overflow: hidden; margin-top: 12px;
  border: 1px solid var(--line-soft); background: #efede8;
}
.jobsheet-dest {
  display: flex; align-items: center; gap: 7px; margin-top: 8px;
  font-size: var(--t-small); font-weight: 600; color: var(--ink); line-height: 1.5;
}
.jobsheet-dest .ic { color: var(--faint); flex: none; }
.eta-edit { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.eta-edit .eta-cap { flex: none; font-size: var(--t-meta); font-weight: 700; color: var(--muted); }
.eta-edit input { flex: 0 1 118px; width: 118px; min-width: 0; text-align: center; padding: 11px 8px; }
.eta-edit .eta-unit { flex: none; font-size: var(--t-meta); color: var(--muted); }
.eta-edit .eta-update { flex: 1; padding: 11px 10px; font-size: var(--t-small); }
/* secondary job actions share one quiet row under the card's facts;
   the state's single primary action keeps the full width below. */
.job-subrow { display: flex; gap: 8px; margin-top: 10px; }
.job-subrow .btn-sub { flex: 1; margin-top: 0; padding: 11px 8px; font-size: var(--t-small); }
/* while a job is active, the online/offline toggle shrinks to a
   quiet control: it governs NEW-work reception only, and the job in
   hand is the dock's main object. */
.w-online-btn.w-online-mini { padding: 10px 14px; font-size: 13px; box-shadow: none; }
/* attribution stays legally present but compact: one small line,
   tucked under the map's top control rows — never a paragraph
   over content. */
#wStandby .maplibregl-ctrl-attrib { font-size: 10px; line-height: 1.35; padding: 2px 6px; background: rgba(255,255,255,.85); }
#wStandby .maplibregl-ctrl-attrib-inner { white-space: normal; }
/* the toggle used to sit ON the badge / menu row (top: safe+106,
   right: 106px) — on the device it crowded straight into the
   メニュー pill and read as one overlapping blob (device report
   2026-11-04). It now hangs clear below every top row: badge/menu
   end ~safe+142, and the demand banner (full-width at safe+156,
   up to ~3 lines for the conditions-missing text) ends ~safe+233.
   Right-aligned with the menu pill, over bare map only. */
#wStandby .maplibregl-ctrl-bottom-right { top: calc(env(safe-area-inset-top, 0px) + 248px); right: 12px; bottom: auto; }
#wStandby .maplibregl-ctrl-bottom-right .maplibregl-ctrl { margin: 0; }
/* even when the partner opens the attribution themselves, its box is
   capped: a small block at the top-right of the map, never a band
   spanning the stage (device screenshot 2026-11-01 09:16). */
#wStandby .maplibregl-ctrl-attrib { max-width: 150px; }

/* ---- partner pass (2026-11-01): deliberate header, recenter,
   expanded menu, unassigned list, work conditions, rating dist ---- */
/* the approved header is a deliberate treatment, not raw text on
   tiles: a quiet frost band behind the title zone that fades into
   the map — the title reads at a glance over any tile colour, and
   the band's foot is clear of the badge / menu row below it. */
body.rm-approved .warm-head {
  /* fully opaque behind the logo/title zone (device review
     2026-11-01: the long fade let map tiles bleed through the
     title and crowd the badge/menu row below it). The fade now
     lives only in the last few px at the band's foot, clear of
     every label. */
  background: linear-gradient(180deg, rgba(244,244,246,1) 0%, rgba(244,244,246,1) 78%, rgba(244,244,246,.92) 100%);
  padding-bottom: 18px;
  margin-bottom: 0;
}
/* the attribution toggle joins the house pill grammar — a quiet
   white circle, never a stray black blob floating on the map. */
#wStandby .maplibregl-ctrl-attrib-button { background-color: rgba(255,255,255,.94); border: 1px solid var(--line-soft); border-radius: 999px; box-shadow: 0 1px 6px rgba(10,30,60,.14); }
/* customer home recenter: a finger-sized bubble glued above the
   home sheet's top-right corner; customer.js mirrors the sheet's
   translateY onto it and sets its bottom from the sheet's height,
   so it rides every snap and drag without fighting either gesture. */
.map-recenter {
  position: absolute; right: calc(var(--s3) + 2px); bottom: 40%;
  /* above the sheet (600): the bubble mirrors the sheet's own
     transform, so at the expanded snap it must paint OVER the
     sheet's top-right corner, never slide underneath it */
  z-index: 615; width: 48px; height: 48px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  /* solid + no will-change: same ghost fix as .sheet — this bubble
     is transform-mirrored every frame of every sheet drag. */
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line-soft);
  box-shadow: 0 6px 16px rgba(10,30,60,.16);
  cursor: pointer; font-family: inherit;
}
.map-recenter:active { filter: brightness(.94); }
/* recenter: a finger-sized bubble riding the dock's top-right
   corner — glued to the sheet cluster at every dock height, clear
   of the map's pan gesture and of every sheet drag. */
.w-recenter {
  position: absolute; top: -58px; right: 2px; z-index: 5;
  width: 48px; height: 48px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.95); color: var(--text);
  border: 1px solid var(--line-soft);
  box-shadow: 0 6px 16px rgba(10,30,60,.16);
  cursor: pointer; font-family: inherit; touch-action: manipulation;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.w-recenter:active { transform: scale(.94); }
.wmenu-group { font-size: var(--t-label); font-weight: 700; color: var(--muted); letter-spacing: .06em; margin: 18px 2px 8px; }
.wmenu-group:first-of-type { margin-top: 4px; }
#wMenuFoot { margin-top: 20px; }
#wMenuFoot .btn-ghost { width: 100%; }
/* unassigned-request rows: the whole row is the tap surface */
.wopen-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  text-align: left; background: var(--surface);
  border: 1px solid var(--line-soft); border-radius: 14px;
  box-shadow: var(--shadow-card);
  padding: 13px 14px; margin-bottom: 10px;
  cursor: pointer; font-family: inherit; color: var(--text);
}
.wopen-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.wopen-cat { font-size: var(--t-body); font-weight: 700; }
.wopen-sub { font-size: var(--t-meta); color: var(--muted); }
.wopen-side { flex: none; display: flex; align-items: center; gap: 6px; }
.wopen-pay { font-size: var(--t-body); font-weight: 800; font-variant-numeric: tabular-nums; }
/* open-request cards (phase C, 2026-11-02): kind chip + the facts a
   partner needs to judge the job from the list itself — symptom,
   area/time, photos — with the pay labeled as what it is （報酬） */
.wopen-top { display: flex; align-items: center; gap: 8px; }
.wopen-chip {
  flex: none; font-size: 10.5px; font-weight: 800; letter-spacing: .05em;
  color: var(--bg); background: var(--text);
  border-radius: 999px; padding: 3px 8px;
}
.wopen-trouble {
  font-size: var(--t-meta); color: var(--text); line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.wopen-paycol { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
.wopen-paylab { font-size: 10px; font-weight: 700; color: var(--muted); letter-spacing: .06em; }
/* slim notice banner on the standby home (phase C): one
   announcement, whole strip is the tap surface */
.wnotice {
  display: flex; align-items: center; gap: 9px; width: 100%;
  margin-top: 10px; padding: 11px 13px;
  background: var(--text); color: var(--bg);
  border: none; border-radius: 14px; cursor: pointer;
  font-family: inherit; text-align: left;
  box-shadow: var(--shadow-card);
}
.wnotice-tag { flex: none; font-size: 10px; font-weight: 800; letter-spacing: .08em; border: 1px solid currentColor; border-radius: 999px; padding: 2px 7px; }
.wnotice-text { flex: 1; min-width: 0; font-size: 12px; font-weight: 700; line-height: 1.4; }
.wnotice .wmenu-chev { color: var(--bg); }
/* explanation guide carousel (phase C): full-screen, one headline,
   a step diagram for a visual, dots, round arrows, one big confirm */
.wguide {
  /* 940: the guide is a modal and must own the surface — above the
     approved-mode header band (700), the notify prompt (800) and
     bottom sheets (900); below the phone gate (960) and the boot
     splash (1200). At 90 the header band painted over the guide's
     top strip and the step chips showed through it half-faded
     (device report 2026-11-04). */
  position: fixed; inset: 0; z-index: 940;
  background: var(--bg); color: var(--text);
  padding: calc(18px + env(safe-area-inset-top)) 22px calc(20px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column;
}
.wguide.hidden { display: none; }
.wguide-x {
  position: absolute; top: calc(12px + env(safe-area-inset-top)); right: 14px;
  width: 38px; height: 38px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--surface); color: var(--text);
  font-size: 18px; line-height: 1; cursor: pointer; font-family: inherit;
}
.wguide-steps { display: flex; flex-wrap: wrap; gap: 6px; margin: 52px 0 26px; }
.wguide-step {
  font-size: 11px; font-weight: 700; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 6px 10px;
  display: inline-flex; align-items: center; gap: 5px;
}
.wguide-step.done { color: var(--muted); border-color: var(--line-soft); }
.wguide-step.done::before { content: "✓"; font-size: 10px; }
.wguide-step.now { background: var(--text); color: var(--bg); border-color: var(--text); }
.wguide-title { font-size: 27px; font-weight: 800; line-height: 1.35; letter-spacing: .01em; white-space: pre-line; margin: 0 0 14px; }
.wguide-body { font-size: 14.5px; line-height: 1.75; color: var(--text); margin: 0; max-width: 34em; }
.wguide-foot { margin-top: auto; }
.wguide-dots { display: flex; gap: 6px; justify-content: center; margin-bottom: 16px; }
.wguide-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--line); }
.wguide-dot.on { background: var(--text); }
.wguide-nav { display: flex; align-items: center; gap: 10px; }
.wguide-arrow {
  flex: none; width: 52px; height: 52px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--surface); color: var(--text);
  font-size: 22px; line-height: 1; cursor: pointer; font-family: inherit;
}
.wguide-arrow:disabled { opacity: .35; cursor: default; }
.wguide-confirm { flex: 1; min-height: 52px; border-radius: 999px; font-size: 15px; }
/* in-app document body (phase C): the terms-box typography, on its
   own screen, scrollable inside the card */
.wdoc-body { max-height: 62dvh; margin-top: 10px; }
/* work-condition category chips: visible choices, tap to toggle */
.wk-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.wk-chip {
  border: 1px solid var(--line); background: var(--surface);
  color: var(--muted); border-radius: 999px;
  padding: 9px 14px; font-size: 13.5px; font-weight: 600;
  cursor: pointer; font-family: inherit; min-height: 40px;
}
.wk-chip.on { background: var(--text); color: var(--bg); border-color: var(--text); }
/* rating distribution bars */
.wdist { margin-top: 14px; display: flex; flex-direction: column; gap: 6px; }
.wdist-row { display: flex; align-items: center; gap: 8px; font-size: var(--t-meta); color: var(--muted); }
.wdist-lab { flex: none; width: 26px; font-weight: 700; color: var(--text); }
.wdist-track { flex: 1; height: 6px; border-radius: 99px; background: var(--surface2); overflow: hidden; }
.wdist-fill { display: block; height: 100%; background: var(--text); border-radius: 99px; }
.wdist-n { flex: none; width: 34px; text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- pull-to-refresh (2026-10-29, owner) ----------
   The indicator is the boot splash's own mark in miniature: the
   same ring cutout on the same ground colour (both read from the
   page's splash by js/pull-refresh.js; the face background set
   here is only the fallback before that runs). It turns WITH the
   pull and keeps turning at the splash's pace while refreshing.
   Below .bsheet (900) and the splash (1200) by design. */
.ptr-chip {
  position: fixed; left: 50%; top: env(safe-area-inset-top, 0px);
  width: 44px; height: 44px; margin-top: 4px; z-index: 860;
  pointer-events: none; opacity: 0;
  transform: translate(-50%, -62px);
  will-change: transform, opacity;
}
.ptr-chip.ptr-anim { transition: transform .38s var(--ease), opacity .3s ease; }
.ptr-face {
  display: block; width: 100%; height: 100%;
  border-radius: 30%; overflow: hidden;
  border: 1px solid var(--line-soft);
  box-shadow: 0 10px 22px rgba(10, 30, 60, .16);
  background: #0a1e3c;
}
.ptr-ring { display: block; width: 100%; height: 100%; will-change: transform; }
.ptr-pill {
  position: fixed; left: 50%; top: calc(env(safe-area-inset-top, 0px) + 12px);
  transform: translate(-50%, -8px); z-index: 861;
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--text); color: var(--bg);
  font-size: 12.5px; font-weight: 600; line-height: 1;
  padding: 9px 14px; border-radius: 999px;
  box-shadow: 0 10px 24px rgba(10, 30, 60, .20);
  opacity: 0; pointer-events: none;
  transition: opacity .3s ease, transform .38s var(--ease);
}
.ptr-pill.show { opacity: 1; transform: translate(-50%, 0); }
/* track screen (2026-11-01): at the default top offset the pill
   lands exactly on the header row and covers the screen title +
   the 最終更新 stamp (measured). While the tracker is open it
   completes below the header instead — over the map, never over
   the title. Other screens keep the standard top placement. */
body:has(#screenTrack.open) .ptr-pill { top: calc(env(safe-area-inset-top, 0px) + 64px); }
@media (prefers-reduced-motion: reduce) {
  .ptr-chip.ptr-anim, .ptr-pill { transition: none; }
}

/* First-run sequence (2026-10-29): the gate's quiet "あとで" escape,
   visible only while registration is step 3 of the in-app sequence. */
.seq-later { display: block; margin: 16px auto 2px; font-size: 13px; }

/* First-purchase offer — 初回30%OFF (2026-10-30). AttA's structure in
   REPAIRMAN's monochrome world: white sheet, ink badge, one big number,
   one primary action. No colour and no gradient — state colours stay
   reserved for state, per the design system. Motion is the bsheet's
   own quiet glide. */
.offer-badge {
  display: inline-block; background: var(--brand); color: #fff;
  font-size: var(--t-label); font-weight: 700; letter-spacing: .14em;
  padding: 5px 11px; border-radius: 999px; white-space: nowrap;
}
.offer-big { font-size: 76px; font-weight: 800; line-height: 1.02; letter-spacing: -.02em; margin-top: 14px; }
.offer-big .offer-pct { font-size: 40px; margin-left: 2px; }
.offer-big .offer-off { font-size: 26px; letter-spacing: .08em; margin-left: 8px; }
.offer-sub { font-size: var(--t-head); font-weight: 700; margin-top: 8px; }
.offer-text { font-size: var(--t-body); line-height: 1.7; margin-top: 12px; }
.offer-note { font-size: var(--t-meta); color: var(--muted); line-height: 1.6; margin-top: 10px; }
.offer-err { font-size: var(--t-small); color: var(--err); line-height: 1.5; margin-top: 8px; }
.offer-err:empty { margin-top: 0; }
.offer-done {
  margin-top: var(--s3); background: var(--surface2); border: 1px solid var(--line-soft);
  border-radius: var(--r-btn); padding: 14px; text-align: center; font-weight: 700;
}
.offer-box {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 14px var(--s4); box-shadow: var(--shadow-card);
}
.offer-box .ob-main { display: flex; align-items: center; gap: 10px; }
.offer-box .ob-big { font-size: 24px; font-weight: 800; letter-spacing: -.01em; }
.offer-box .ob-t { font-weight: 700; font-size: var(--t-body); }
.offer-box .ob-note { font-size: var(--t-small); color: var(--muted); line-height: 1.6; margin-top: 6px; }
.offer-box.used { color: var(--muted); font-size: var(--t-small); }

/* ---------- receipt （領収書） 2026-10-31 ----------
   Owner spec: issued under the work-profile 宛名; BEFORE issuance the
   user-owned fields （宛名・但し書き） are solid and tappable while the
   fixed fields （金額・明細・日時・発行者） sit faint under frosted
   glass; AT issuance the paper hangs from the slot on its
   perforation stub, and the user tears it free by hand — the
   perforation snapping hole by hole （ブチブチ） under the drag, or
   with a plain tap. Monochrome, small, quiet スッ motion only. */
.btn-bubble {
  display: flex; align-items: center; justify-content: center; gap: var(--s2);
  width: 100%; min-height: 54px; padding: 14px 22px; margin-top: var(--s3);
  border-radius: 999px; background: var(--accent); color: #fff;
  border: 1px solid var(--accent);
  font-weight: 700; font-size: var(--t-title); letter-spacing: .02em; text-align: center;
  box-shadow: 0 8px 22px rgba(10,30,60,.20);
  transition: transform .15s var(--ease), background-color .2s, box-shadow .25s, opacity .2s;
}
.btn-bubble:active { transform: scale(.982); background: var(--accent-deep); }
.btn-bubble:disabled { opacity: .5; box-shadow: none; }
.btn-bubble.ghost { background: #fff; color: var(--text); border-color: var(--line); box-shadow: var(--shadow-card); }
.btn-bubble.ghost:active { background: var(--surface2); }

.rc-lead { font-size: var(--t-small); color: var(--muted); line-height: 1.65; margin: 2px 0 12px; }
.rc-chip {
  display: inline-flex; align-items: center; padding: 3px 9px; border-radius: 999px;
  font-size: var(--t-label); font-weight: 700; letter-spacing: .04em; white-space: nowrap;
}
.rc-chip.edit { border: 1px solid var(--brand); color: var(--brand); }
.rc-chip.lock { border: 1px solid rgba(10,30,60,.28); color: rgba(10,30,60,.55); }
.rc-zone-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 4px; }
.rc-zone-head .name { margin: 0; }

/* locked zone: frosted glass over a faint patterned backdrop — the
   values are visible but thin, and the whole card ignores touches. */
.rc-glass-wrap { position: relative; border-radius: var(--r-card); overflow: hidden; margin-bottom: 10px; }
.rc-glass-wrap::before {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(130px 70px at 16% 26%, rgba(10,30,60,.16), transparent 70%),
    radial-gradient(170px 90px at 86% 78%, rgba(10,30,60,.12), transparent 70%),
    repeating-linear-gradient(45deg, rgba(10,30,60,.055) 0 10px, transparent 10px 22px);
}
.rc-glass {
  position: relative; padding: 14px var(--s4);
  background: rgba(255,255,255,.55);
  backdrop-filter: blur(9px) saturate(1.05); -webkit-backdrop-filter: blur(9px) saturate(1.05);
  border: 1px solid rgba(10,30,60,.10); border-radius: var(--r-card);
  color: rgba(10,30,60,.58); pointer-events: none; user-select: none; -webkit-user-select: none;
}
.rc-glass .breakdown .line { border-bottom-color: rgba(10,30,60,.10); }
.rc-glass .kv { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; font-size: var(--t-small); border-bottom: 1px solid rgba(10,30,60,.08); }
.rc-glass .kv:last-child { border-bottom: none; }
.rc-glass .kv b { font-weight: 600; text-align: right; }

/* the paper: hangs from the slot on its stub, attached at the
   perforation (the holes row straddles the seam). The sheet below
   the line is the user's to tear off; .rc-freed is the end state. */
.rc-stage { position: relative; margin-bottom: 12px; }
.rc-hanger { position: relative; }
.rc-slot {
  position: absolute; top: 0; left: 6px; right: 6px; height: 11px; z-index: 4;
  background: linear-gradient(#33507c, #0a1e3c); border-radius: 6px;
  box-shadow: 0 4px 12px rgba(10,30,60,.35);
}
.rc-stub {
  position: relative; z-index: 2; height: 32px; margin-top: 6px;
  background: #fff; border: 1px solid var(--line); border-bottom: none;
  border-radius: 8px 8px 0 0;
}
.rc-perfline {
  position: absolute; top: 31px; left: 0; right: 0; z-index: 3;
  height: 15px; padding: 0 24px;
  display: flex; align-items: center; justify-content: space-between;
  pointer-events: none;
}
.rc-perfline::before {
  content: ""; position: absolute; left: 10px; right: 10px; top: 50%;
  border-top: 1.5px dashed var(--line);
}
.rc-hole {
  position: relative; flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--bg);
  box-shadow: inset 0 1px 2px rgba(10,30,60,.35), 0 0 0 1px rgba(10,30,60,.05);
  transition: height .12s ease, background-color .12s ease, box-shadow .12s ease;
}
.rc-hole.snapped {
  height: 13px; border-radius: 3px;
  background: rgba(10,30,60,.10); box-shadow: none;
}
/* torn paper fringe, revealed along the tear front (--front) on the
   stub's lower edge and the sheet's top edge. */
.rc-jag {
  position: absolute; left: 0; height: 7px; width: var(--front, 0%);
  background: #fff; pointer-events: none;
  clip-path: polygon(0 0, 100% 0, 100% 55%, 96% 95%, 91% 40%, 86% 88%, 80% 45%, 75% 92%, 69% 38%, 64% 85%, 58% 48%, 53% 94%, 47% 42%, 42% 88%, 36% 35%, 31% 82%, 25% 48%, 20% 90%, 14% 40%, 9% 85%, 4% 45%, 0 70%);
}
.rc-jag-stub { bottom: -6px; z-index: 2; }
.rc-jag-sheet { top: -6px; z-index: 2; }
.rc-paper {
  position: relative; z-index: 1;
  background: #fff; border: 1px solid var(--line); border-top: none;
  border-radius: 0 0 8px 8px;
  /* the shadow deepens and spreads as the sheet peels (--curl 0..1,
     driven 1:1 by the tearing finger in JS) */
  box-shadow: 0 calc(12px + 30px * var(--curl, 0)) calc(32px + 24px * var(--curl, 0)) rgba(10,30,60, calc(.12 + .08 * var(--curl, 0)));
  padding: 16px 18px 18px;
}
/* fold shading: dark in the bend at the hinge, easing down the
   sheet — sells the curl on top of the rotateX geometry. */
.rc-paper::after {
  content: ""; position: absolute; inset: 0; z-index: 3;
  border-radius: inherit; pointer-events: none;
  background: linear-gradient(180deg,
    rgba(10,30,60, calc(.17 * var(--curl, 0))) 0%,
    rgba(10,30,60, calc(.05 * var(--curl, 0))) 30%,
    rgba(255,255,255,0) 58%,
    rgba(10,30,60, calc(.06 * var(--curl, 0))) 100%);
}
.rc-stage:not(.rc-freed) .rc-paper {
  touch-action: none; cursor: grab;
  user-select: none; -webkit-user-select: none;
}
.rc-stage:not(.rc-freed) .rc-paper:active { cursor: grabbing; }
.rc-grip { display: flex; justify-content: center; padding: 14px 0 4px; }
.rc-grip span { width: 46px; height: 5px; border-radius: 999px; background: rgba(10,30,60,.16); }
.rc-tearhint { margin: 12px 0 2px; text-align: center; }
.rc-tearhint-main { display: block; font-size: var(--t-small); font-weight: 700; }
.rc-tearhint-sub { display: block; font-size: var(--t-label); color: var(--muted); margin-top: 3px; }
/* torn free: the stub stays behind at the slot with its torn edge;
   the sheet settles below it as the free receipt, still leaning
   toward the user from the flip that freed it. */
.rc-stage.rc-freed .rc-perfline,
.rc-stage.rc-freed .rc-grip,
.rc-stage.rc-freed .rc-tearhint { display: none; }
.rc-stage.rc-freed .rc-paper {
  margin-top: 12px; border-top: 1px solid var(--line); border-radius: 8px;
  transform: perspective(880px) rotateX(7deg);
  transform-origin: 50% 0;
}
.rc-delivery { animation: rcCardIn .45s var(--ease) both; }
@keyframes rcCardIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.rc-brand { font-size: 11px; letter-spacing: .34em; color: var(--muted); font-weight: 700; text-align: center; margin-top: 12px; }
.rc-title { font-size: 20px; font-weight: 800; letter-spacing: .14em; text-align: center; margin: 4px 0 12px; }
.rc-recipient { font-size: var(--t-head); font-weight: 700; border-bottom: 1.5px solid var(--text); padding-bottom: 6px; }
.rc-recipient small { font-size: var(--t-small); font-weight: 600; margin-left: 6px; }
.rc-amount { display: flex; align-items: baseline; justify-content: space-between; margin: 14px 0 2px; }
.rc-amount span { font-size: var(--t-small); color: var(--muted); }
.rc-amount b { font-size: 29px; font-weight: 800; letter-spacing: .01em; }
.rc-note-line { font-size: var(--t-small); color: var(--muted); margin-top: 8px; line-height: 1.6; }
.rc-paper .breakdown { margin-top: 10px; }
.rc-paper .kv { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; font-size: var(--t-small); color: var(--muted); }
.rc-paper .kv b { color: var(--text); font-weight: 600; text-align: right; }
.rc-issued-note { font-size: var(--t-small); color: var(--muted); line-height: 1.65; margin-top: 2px; }
.rc-mail-status { display: flex; align-items: center; gap: 8px; font-size: var(--t-small); margin-top: 10px; }
.rc-mail-status .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--faint); flex: none; }
.rc-mail-status.sent .dot { background: var(--go); }

/* the tear itself is gesture-driven in JS — there is no canned
   animation to disable; under reduced motion the tear completes
   instantly and the delivery card appears without its rise. */
@media (prefers-reduced-motion: reduce) {
  .rc-delivery { animation: none; }
  .rc-hole { transition: none; }
  .rc-stage.rc-freed .rc-paper { transform: none; }
}

/* ---- pre-registration paywall (2026-11-02) ----
   The subscription-paywall grammar (tier rows, billing rows, one
   big CTA) in REPAIRMAN's own monochrome system: ink selection,
   quiet cards, colour only on the assurance line. Rows are full
   finger-sized tap surfaces, per the page-organization law. */
.prereg-head { display: flex; align-items: center; gap: 12px; margin: 4px 0 16px; }
.prereg-mark { display: block; width: 46px; height: 46px; border-radius: 13px; box-shadow: 0 2px 8px rgba(10,30,60,.18), inset 0 0 0 1px rgba(10,30,60,.05); }
.prereg-brand { font-size: var(--t-title); font-weight: 800; letter-spacing: .045em; line-height: 1.1; }
.prereg-brand-jp { font-size: 9.5px; font-weight: 600; letter-spacing: .24em; color: var(--muted); margin-left: 8px; }
.prereg-sub { font-size: var(--t-small); color: var(--muted); margin-top: 3px; }
.prereg-banner { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-card); padding: 14px 16px; box-shadow: var(--shadow-card); cursor: pointer; }
.prereg-banner-ic { flex: none; width: 38px; height: 38px; border-radius: 50%; background: var(--brand-tint); display: grid; place-items: center; color: var(--brand); }
.prereg-banner-tx { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.prereg-banner-title { font-size: 14px; font-weight: 700; }
.prereg-banner-sub { font-size: var(--t-meta); color: var(--muted); line-height: 1.5; }
.prereg-banner .chev { flex: none; color: var(--faint); display: inline-flex; }
.prereg-opts { display: flex; flex-direction: column; gap: 10px; }
.prereg-opt { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 58px; text-align: left; background: var(--surface); border: 1.5px solid var(--line-soft); border-radius: var(--r-card); padding: 13px 15px; cursor: pointer; transition: border-color .2s, background-color .2s, transform .15s var(--ease); }
.prereg-opt.on { border-color: var(--text); }
.prereg-radio { flex: none; width: 20px; height: 20px; border-radius: 50%; border: 2px solid var(--line); background: var(--surface); }
.prereg-opt.on .prereg-radio { border-color: var(--text); background: var(--text); box-shadow: inset 0 0 0 4px var(--surface); }
.prereg-opt-main { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.prereg-opt-name { font-size: var(--t-body); font-weight: 700; line-height: 1.35; }
.prereg-opt-sub { font-size: var(--t-meta); color: var(--muted); line-height: 1.5; }
.prereg-opt-price { flex: none; font-size: 17px; font-weight: 800; font-variant-numeric: tabular-nums; }
.prereg-opt-price small { font-size: var(--t-meta); font-weight: 600; color: var(--muted); margin-left: 1px; }
.prereg-badge { display: inline-block; vertical-align: 2px; background: var(--text); color: #fff; font-size: 10.5px; font-weight: 700; letter-spacing: .02em; border-radius: 999px; padding: 3px 8px; margin-left: 4px; white-space: nowrap; }
.prereg-trust { display: flex; gap: 10px; align-items: flex-start; background: var(--surface2); border: 1px solid var(--line-soft); border-radius: var(--r-card); padding: 13px 15px; margin-top: 14px; font-size: var(--t-small); font-weight: 600; line-height: 1.6; }
.prereg-trust svg { flex: none; width: 20px; height: 20px; color: var(--go); margin-top: 1px; }

/* ---------- business support / backers (2026-11-05) ----------
   Minimal additions only: amount chips in the .prereg-opt spirit
   (selected = accent frame on accent tint), the progress bar and
   the gift / name rows. Cards, labels, notes and buttons reuse the
   shared classes. */
.backer-amts { display: flex; flex-wrap: wrap; gap: 8px; }
.backer-amt { flex: 1 1 30%; min-height: 48px; padding: 10px 8px; background: var(--surface); border: 1.5px solid var(--line-soft); border-radius: var(--r-input); font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--text); cursor: pointer; transition: border-color .2s, background-color .2s, transform .15s var(--ease); }
.backer-amt.on { border-color: var(--accent); background: var(--accent-tint); color: var(--brand); }
.backer-bar { height: 10px; border-radius: 999px; background: var(--surface2); border: 1px solid var(--line-soft); overflow: hidden; margin-top: 10px; }
.backer-bar-fill { height: 100%; border-radius: 999px; background: var(--accent); transition: width .4s var(--ease); }
.backer-gifts { margin-top: 2px; }
.backer-gift { font-size: var(--t-meta); color: var(--muted); line-height: 1.55; padding: 7px 0; border-top: 1px solid var(--line-soft); }
.backer-gifts .backer-gift:first-child { border-top: none; padding-top: 2px; }
.backer-names { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 8px; }
.backer-name { font-size: var(--t-small); font-weight: 600; }
.backer-off { opacity: .45; }

/* ---------- design-conformance pass (2026-11-02) ----------
   Nav stacks: a screen parked in an inactive tab's stack stays
   mounted (its state is preserved) but is not painted. */
.screen.nav-away { display: none !important; }

/* Home sheet: one rhythm between the sheet's sections (the mounts
   render empty when they have nothing to say; adjacent margins
   collapse through the empty boxes, so the visible gap stays one
   --gap-card, never a double). */
.sheet-scroll > * + * { margin-top: var(--gap-card); }

/* My Page, profile type (Skyward Profile shape): identity first,
   then THREE big numbers, then the grouped rows. */
.prof-stats { display: flex; padding: 18px 6px 16px; }
.pstat { flex: 1 1 0; min-width: 0; text-align: center; }
.pstat + .pstat { border-left: 1px solid var(--line-soft); }
.pstat-num { font-size: 27px; font-weight: 800; letter-spacing: -.01em; line-height: 1.15; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pstat-num.small { font-size: 19px; padding-top: 5px; }
.pstat-label { font-size: var(--t-label); color: var(--muted); margin-top: 3px; }

/* Tracking, boarding-pass type: one ticket — head band with the
   request code, the big status in the main zone, a perforation,
   the ticket grid （現場・内容・受付日時）, a second perforation,
   and the status flow always on the face. */
.pass { background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-card); box-shadow: var(--shadow-card); margin-bottom: var(--gap-card); overflow: hidden; }
.pass-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px var(--s4) 11px; border-bottom: 1px solid var(--line-soft); }
.pass-brand { font-size: var(--t-label); font-weight: 800; letter-spacing: .18em; }
.pass-code { font-size: var(--t-meta); font-weight: 600; color: var(--muted); white-space: nowrap; }
.pass-main { padding: 16px var(--s4) 2px; }
.pass-perf { position: relative; border-top: 2px dashed var(--line); margin-top: 14px; }
.pass-perf::before, .pass-perf::after { content: ""; position: absolute; top: -10px; width: 20px; height: 20px; border-radius: 50%; background: var(--bg); }
.pass-perf::before { left: -11px; }
.pass-perf::after { right: -11px; }
.pass-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px; padding: 14px var(--s4) 16px; }
.pass-grid .wide { grid-column: 1 / -1; }
.pass-k { font-size: var(--t-label); font-weight: 600; letter-spacing: .05em; color: var(--muted); }
.pass-v { font-size: 14px; font-weight: 600; line-height: 1.45; margin-top: 2px; overflow-wrap: anywhere; }
.pass-flow { padding: 4px var(--s4) 14px; }
.pass-flow .flowline { margin: 10px 0 0; }

/* ---------- navy x orange pass (2026-11-04, owner decision) ----------
   The token change above recolours everything that already spoke
   var(--brand) / var(--text). The rules below recolour the components
   whose structure the tokens cannot reach: the dock (navy glass,
   orange active pill), the screen head band (navy), the splash (navy
   world, orange ring glow, the brand line under the name), the member
   gate (navy street-grid world, the form on a white card), the map
   pins and searching rings (orange = live), the worker online badge,
   the key totals, and the boarding-pass head band. Proportions,
   wording and behaviour are untouched. */
.tabbar {
  background: rgba(8, 24, 48, .92);
  border-color: rgba(255, 255, 255, .10);
  box-shadow: 0 18px 44px rgba(7, 22, 48, .38), 0 3px 10px rgba(7, 22, 48, .22);
}
.tab-pill { background: var(--accent); box-shadow: 0 6px 16px rgba(255, 106, 0, .38); }
.tabbar button { color: #8fa3c0; }
.tabbar button .ic { color: #8fa3c0; }
.tabbar button.on { color: #fff; }
.tabbar button.on .ic { color: #fff; }
.tab-badge { background: var(--accent); border-color: #0a1e3c; }
.screen .head {
  background: linear-gradient(180deg, #0e2a52, #0a1e3c);
  border-bottom: 1px solid rgba(255, 255, 255, .08);
  color: #fff;
}
.screen .head .progress { color: rgba(255, 255, 255, .62); }
.screen .head .icon-btn {
  background: rgba(255, 255, 255, .13); color: #fff;
  border: 1px solid rgba(255, 255, 255, .16); box-shadow: none;
}
.screen .head .tts-toggle[aria-pressed="false"] { color: rgba(255, 255, 255, .45); }
.boot-splash { background: linear-gradient(180deg, #071630 0%, #0a1e3c 55%, #0e2a52 100%); --bs-orange: #ff6a00; }
.boot-splash::before { background: radial-gradient(58% 46% at 50% 42%, rgba(255, 106, 0, .16), rgba(255, 106, 0, 0) 70%); }
.bs-halo { background: radial-gradient(circle, rgba(255, 106, 0, .30) 0%, rgba(255, 106, 0, .11) 42%, rgba(255, 106, 0, 0) 68%); }
.bs-mark { border-color: rgba(255, 255, 255, .16); box-shadow: 0 24px 54px rgba(2, 8, 20, .50), 0 4px 14px rgba(2, 8, 20, .35); }
#bootSplash .bs-face, #wSplash .bs-face, #sSplash .bs-face, #aSplash .bs-face { background: linear-gradient(155deg, #14335f, #071630); }
.bs-wm { color: #fff; }
.bs-wm-jp { color: rgba(255, 255, 255, .55); }
.bs-role { color: rgba(255, 255, 255, .62); }
.bs-text { color: rgba(255, 255, 255, .78); }
/* the platform's core line, under the splash lockup (2026-11-04) */
.bs-tag { margin-top: 16px; font-size: 12.5px; font-weight: 700; letter-spacing: .14em; color: rgba(255, 255, 255, .92); white-space: nowrap; animation: bsFade 1s ease 1.15s both; }
.boot-splash.error .bs-tag { animation: none; opacity: 1; transform: none; }
#screenAuth, #screenPhoneGate { background:
  radial-gradient(120% 60% at 85% -5%, rgba(255, 106, 0, .13), rgba(255, 106, 0, 0) 60%),
  repeating-linear-gradient(0deg, rgba(255, 255, 255, .05) 0 1px, transparent 1px 56px),
  repeating-linear-gradient(90deg, rgba(255, 255, 255, .05) 0 1px, transparent 1px 56px),
  linear-gradient(180deg, #071630, #0e2a52);
}
#wPhoneGate { background:
  radial-gradient(120% 60% at 85% -5%, rgba(255, 106, 0, .13), rgba(255, 106, 0, 0) 60%),
  repeating-linear-gradient(0deg, rgba(255, 255, 255, .05) 0 1px, transparent 1px 56px),
  repeating-linear-gradient(90deg, rgba(255, 255, 255, .05) 0 1px, transparent 1px 56px),
  linear-gradient(180deg, #071630, #0e2a52);
}
#screenAuth .auth-wrap, #screenPhoneGate .auth-wrap {
  background: var(--surface); border-radius: 26px;
  padding: 22px 18px 20px; margin-top: 26px;
  box-shadow: 0 24px 54px rgba(2, 8, 20, .42), 0 3px 10px rgba(2, 8, 20, .25);
}
#screenAuth .wh-wm { color: #fff; }
#screenAuth .wh-wm-jp { color: rgba(255, 255, 255, .55); }
#screenAuth .wh-title { color: #fff; }
#screenAuth .wh-sub { color: rgba(255, 255, 255, .72); }
#screenAuth .wh-chip { background: rgba(255, 255, 255, .12); border-color: rgba(255, 255, 255, .20); color: #fff; box-shadow: none; }
#screenAuth .wh-copy-line { margin-top: 18px; font-size: 13px; font-weight: 800; letter-spacing: .13em; color: #fff; }
.pinwrap .dotcore { background: var(--accent); }
.map-pill .dot { background: var(--accent); }
.track-pin .srings i { border-color: rgba(255, 106, 0, .55); }
.partner-pin .dotcore { background: var(--accent); border-color: #fff; }
.partner-pin .ptag { background: var(--accent); }
.partner-pin.stale .dotcore { background: #fff; border-color: #a7adb8; }
.wmap-top .status-badge.go { color: var(--accent-deep); }
.wmap-top .status-badge.go::before { background: var(--accent); }
.w-brand-copy { align-self: center; background: rgba(255, 255, 255, .88); border-radius: 999px; padding: 5px 14px; font-size: 11.5px; font-weight: 800; letter-spacing: .12em; color: var(--brand); pointer-events: none; }
.brand-copy { font-size: 12px; font-weight: 800; letter-spacing: .13em; color: var(--brand); }
.home-copy { margin: 2px 2px 12px; }
.qb-total b { color: var(--accent); }
.pay-total b { color: var(--accent); }
.breakdown .total > span:last-child { color: var(--accent); }
.pass-head { background: linear-gradient(180deg, #0e2a52, #0a1e3c); border-bottom: none; }
.pass-brand { color: #fff; }
.pass-code { color: rgba(255, 255, 255, .65); }
.sheet-hero { background: #edf2f9; }
body:has(.page-wrap) { background: #e9eff6; }
