/* DoonggriCar app shell stylesheet.
 * Hand-written, no framework, no @import from another origin.
 *
 * Visual language (2026-09-21, 사장님이 시안 보고 고른 「밝고 여백 많은」 안 —
 * 시안 원본은 docs/design/mockup-2026-09-21.html):
 *   - 밝은 회색 배경 위에 흰 카드. 카드에 테두리를 두지 않고 배경 대비로만 구분한다.
 *   - 강조색은 둥그리 초록 하나뿐. 상태를 색으로 말할 때만 쓰고 장식에는 안 쓴다.
 *   - 글자 크기 대비를 크게. 배터리 %가 화면에서 가장 큰 글자다.
 *   - 짧은 값(보안·공조·누적)은 카드를 따로 만들지 않고 한 카드 안에서 얇은 줄로 나눈다.
 *   - 아이콘은 이모지가 아니라 선 아이콘(인라인 SVG). 이모지는 기기마다 그림이 달라진다.
 * 다크 모드는 토큰만 갈아끼운다 — 구조/간격은 한 벌만 존재한다. 밤 운전석에서
 * 흰 화면은 눈이 부시므로 다크를 없애지 않는다.
 *
 * Three layouts share these rules:
 *   - phone   (default / narrow):        single column, thumb-reachable controls
 *   - desktop (min-width: 1024px, fine pointer): wider content, more breathing room
 *   - car     (coarse pointer + landscape + wide + tall, OR html[data-layout="car"]):
 *             large tap targets, bigger body text, for the in-car touchscreen
 *
 * The car layout is triggered two ways (see base.html / frontend/index.html for
 * the tiny inline script that sets data-layout):
 *   1. Automatically, via `@media (pointer: coarse) and (orientation: landscape)
 *      and (min-width: 900px) and (min-height: 600px)` — this matches the
 *      Tesla browser (a wide, landscape, touch-only Chromium) without ever
 *      reading the User-Agent string, which Tesla's browser does not
 *      reliably/consistently expose.
 *      `min-height` is load-bearing, not decoration: a current large phone
 *      held sideways is ~930x430 CSS px and therefore satisfies coarse +
 *      landscape + 900px wide all by itself. That was merely cosmetic while
 *      the car layout only changed sizes, but since 2026-09-21 it also
 *      DROPS the 운행/설정 tabs from the DOM (Nav.tsx), so a false positive
 *      would cost the owner two of four tabs every time they rotated their
 *      phone. The car screen is ~800 CSS px tall, a phone in landscape
 *      ~430, so height is what actually separates the two.
 *   2. Explicitly, via a `?layout=car` or `?layout=standard` URL flag that
 *      is remembered (localStorage) and applied as html[data-layout=...],
 *      which wins over the automatic media query. This covers the case
 *      where auto-detection is wrong (e.g. a touchscreen desktop monitor)
 *      or the owner just wants to force one look from the phone.
 *
 * This file is shared by the React app AND the pages that are still
 * server-rendered Jinja (/login, /passkeys, /places, /afterblow). Class
 * names are therefore a contract with both — renaming one silently breaks
 * a template. `.app-header-app` is the one class only React sets; see the
 * header section for why.
 */

:root {
  color-scheme: light dark;

  /* --- surfaces --------------------------------------------------- */
  /* 2026-11-08 (사장님: 「UI도 개선하라고 했는데」): grouped sections again --
     soft white surfaces (radius 16, no border, no heavy shadow) on a light
     grey page, rows inside separated by hairlines like iOS inset-grouped
     lists. The 2026-10-08 flat pass kept the rows; this only puts them on
     surfaces. --color-surface is the group; --color-bg is the page. */
  --color-bg: #f2f4f6;          /* page */
  --color-surface: #ffffff;     /* grouped section (.card, menu list, controls) */
  --color-bg-elevated: #ffffff; /* raised surface (seg thumb, bars, sheets) */
  --color-bg-sunken: #f2f4f6;   /* inputs, tonal buttons -- sits ON a surface */
  --color-fill: #e8ebee;        /* segmented-control track ON the page */
  --color-border: #eceef1;      /* hairlines between rows */

  /* --- text ------------------------------------------------------- */
  /* Three steps only (2026-11-08: was several near-equal greys). */
  --color-text: #191f28;        /* primary */
  --color-text-muted: #4e5968;  /* secondary -- 7.4:1 on white, 6.9:1 on the page */
  --color-text-faint: #6b7684;  /* tertiary (units, timestamps) -- 4.6:1 on white */

  /* --- accents ---------------------------------------------------- */
  --color-primary: #0f8a5f;
  --color-primary-soft: #e7f5ef;
  --color-primary-contrast: #ffffff;
  --color-danger: #e5484d;
  --color-danger-soft: #fff1f1;
  --color-danger-contrast: #ffffff;

  --color-warning-bg: #fff8e6;
  --color-warning-text: #8a5a00;
  --color-warning-border: #ffe1a3;

  /* --- type ------------------------------------------------------- */
  /* Five steps (2026-11-08). Everything else derives from these.
     display = battery % only; hero = a screen's one total. */
  --font-size-display: 56px;
  --font-size-hero: 34px;
  --font-size-title: 26px;      /* screen title (h1) */
  --font-size-section: 18px;    /* section title inside/above a group */
  --font-size-body: 16px;
  --font-size-small: 13px;      /* caption */

  /* --- geometry --------------------------------------------------- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 32px;

  --radius-sm: 10px;
  --radius-md: 12px;
  --radius-lg: 20px;
  --radius-group: 16px; /* grouped surfaces (2026-11-08) */
  --radius-pill: 999px;

  --tap-target: 48px;

  --max-content-width: 640px;

  /* Bottom tab bar height (phone) -- icon + label stacked, taller than a
     bare --tap-target so the label doesn't feel cramped. */
  --bottom-nav-height: 60px;

  /* The only shadow in the file. Cards are separated by the bg/surface
     contrast, not by depth; this is just enough to stop a white card from
     dissolving into a light background on a washed-out phone screen. */
  --shadow-card: 0 1px 2px rgba(25, 31, 40, 0.04);

  /* Icon chip behind list-row icons (24px viewBox, 1.8 stroke everywhere). */
  --icon-chip: 36px;
  --icon-chip-bg: #f2f4f6;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #0b0d10;
    --color-surface: #17191e;
    --color-bg-elevated: #1c1f25;
    --color-bg-sunken: #23272e;
    --color-fill: #1c1f25;
    --color-border: #262a31;

    --color-text: #eceff3;
    --color-text-muted: #a9b1bb;
    --color-text-faint: #848d98;
    --icon-chip-bg: #23272e;

    /* Lifted for contrast against the dark surface — the light-mode green
       fails to read on #171A20. */
    --color-primary: #2fbf85;
    --color-primary-soft: #142a20;
    --color-primary-contrast: #06130d;
    --color-danger: #ff6369;
    --color-danger-soft: #2a1618;
    --color-danger-contrast: #1a0708;

    --color-warning-bg: #2a2110;
    --color-warning-text: #ffc248;
    --color-warning-border: #4a3a17;

    --shadow-card: none;
  }
}

/* ---------- base ---------- */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  overflow-x: hidden;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Malgun Gothic",
    "맑은 고딕", "Noto Sans KR", "Segoe UI", sans-serif;
  font-size: var(--font-size-body);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

/* iOS PWA: black-translucent status bar draws over the page (viewport-fit=cover).
   Keep the page below it, and paint a solid strip there so scrolled content
   never shows through the clock/battery. 0 everywhere else. */
body {
  padding-top: env(safe-area-inset-top);
}

body::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: env(safe-area-inset-top);
  background: var(--color-bg);
  z-index: 35;
  pointer-events: none;
}

/* iOS zooms the whole page in when a text control under 16px gets focus, and a
   home-screen app then stays zoomed (the fixed tab bar no longer meets the screen
   bottom). Several controls are 13-15px by design, so floor them on touch phones.
   The car layout (wide) is excluded; pinch-zoom itself stays enabled. */
@media (max-width: 640px) and (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
  select,
  textarea {
    font-size: 16px !important;
  }
}

a {
  color: var(--color-primary);
  text-decoration: none;
}

h1,
h2,
h3 {
  margin: 0;
  letter-spacing: -0.02em;
  font-weight: 800;
}

h1 {
  font-size: 1.375rem;
}

h2 {
  font-size: 1.0625rem;
}

h3 {
  font-size: 0.9375rem;
}

p {
  margin: 0;
}

ul {
  margin: 0;
  padding-left: 1.1em;
}

.text-muted {
  color: var(--color-text-muted);
}

/* Inside a card, a horizontal rule is a row divider, not decoration. */
hr {
  height: 1px;
  border: 0;
  margin: var(--space-4) 0;
  background: var(--color-border);
}

/* ---------- header ----------
 * Two callers, deliberately different:
 *   - Jinja base.html renders `.app-header` on /login, /passkeys, /places,
 *     /afterblow. Those pages have no bottom tab bar, so their header is
 *     the ONLY way back to the app and must stay visible everywhere.
 *   - React's Nav.tsx renders `.app-header.app-header-app`, which is
 *     hidden on phones: the SPA screens put their own title in the body
 *     (`.page-header`, see the 시안) and the tabs live in the bottom bar,
 *     so a second brand bar would just eat vertical space. It comes back
 *     on desktop/car, where the tabs sit inside it.
 */

.app-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-bg-elevated);
  border-bottom: 1px solid var(--color-border);
}

.app-header .brand {
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--color-text);
}

.app-header-app {
  display: none;
}

/* ---------- tabs (bottom bar on phone, top row on PC/car) ----------
 * 4 tabs (상태/운행/충전/설정, docs/SCREENS.md §1) rendered TWICE by
 * Nav.tsx -- once into `.bottom-tab-bar`, once into `.main-nav` (inside
 * `.app-header`) -- and exactly one of the two is shown at a time, using
 * `display: none` (which also removes the hidden copy from the tab order
 * and the accessibility tree, so there is only ever one reachable nav).
 * 운행/설정 being absent from the DOM entirely in the car layout is
 * handled in Nav.tsx (JS-level), not here.
 */

.main-nav {
  display: none;
  gap: var(--space-1);
  margin-left: auto;
}

.bottom-tab-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  background: var(--color-bg-elevated);
  border-top: 1px solid var(--color-border);
  /* iOS PWA home-indicator clearance -- index.html's viewport meta has
     viewport-fit=cover, which is required for this to resolve to anything
     nonzero. */
  padding-bottom: env(safe-area-inset-bottom);
}

/* iOS 26 home-screen app: right after a page change a fixed bottom:0 bar can
   stop short of the screen edge until the next scroll (WebKit viewport
   recalculation). Paint the bar's background on below it so that transient
   gap never shows the page through -- invisible everywhere else. */
.bottom-tab-bar::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 120px;
  background: var(--color-bg-elevated);
  pointer-events: none;
}

.tab-link {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: var(--tap-target);
  padding: var(--space-1) var(--space-2);
  text-decoration: none;
  color: var(--color-text-faint);
  font-size: 11px;
  font-weight: 700;
  touch-action: manipulation;
}

.tab-link-icon {
  display: block;
  width: 22px;
  height: 22px;
}

/* NavLink sets aria-current="page" on the active tab -- same signal used
   for every other active/inactive distinction, no extra class needed. */
.tab-link[aria-current="page"] {
  color: var(--color-primary);
}

.tab-link:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}

/* Top-row variant (desktop/car, inside .app-header): horizontal, pill
   shaped, icon beside the label instead of stacked above it. */
.main-nav .tab-link {
  flex: 0 0 auto;
  flex-direction: row;
  gap: var(--space-2);
  min-height: 44px;
  padding: 0 var(--space-4);
  border-radius: var(--radius-pill);
  font-size: 0.9375rem;
  color: var(--color-text-muted);
}

.main-nav .tab-link-icon {
  width: 20px;
  height: 20px;
}

.main-nav .tab-link[aria-current="page"] {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

/* ---------- page shell ---------- */

.content {
  max-width: var(--max-content-width);
  margin: 0 auto;
  padding: var(--space-6) var(--space-5);
  /* Reserve space for the fixed bottom tab bar so it never covers the last
     card -- reset to plain padding wherever .bottom-tab-bar is hidden. */
  padding-bottom: calc(var(--bottom-nav-height) + env(safe-area-inset-bottom) + var(--space-6));
}

/* Under the iOS PWA status bar (body already clears it) the usual 24px top gap
   reads as too much: keep 12px there, 24px everywhere the inset is 0.
   Browsers without max() keep the 24px above. */
.content {
  padding-top: max(var(--space-3), calc(var(--space-6) - env(safe-area-inset-top)));
}

/* The screen's own title line. On phone this replaces the app bar, so it
   is the first thing in the body — see the header section above. */
.page-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.page-header h1 {
  font-size: 1.375rem;
}

.page-header p {
  margin-top: var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

/* ---------- section (was: card) ----------
 * 2026-10-08: no boxed cards any more (owner: buttons and card areas were
 * eating the screen). `.card` keeps its name -- it is a contract with the
 * server-rendered Jinja pages too (login, passkeys) -- but it is now a flat
 * section: no fill, no radius, no shadow, just spacing. Rows inside are
 * separated by hairlines (.stat-row / .session-row / .list-row). */
.card {
  margin-bottom: var(--space-7);
}

.card .card {
  margin-bottom: 0;
}

/* A section's heading is a quiet label above its rows, not a headline. */
.card-title {
  margin: 0 0 var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
  letter-spacing: 0;
}

/* ---------- forms ---------- */

.stack-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.stack-form label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: 0.9375rem;
  font-weight: 600;
}

.input-field {
  min-height: var(--tap-target);
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
  color: var(--color-text);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
}

.input-field:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 0;
}

select.input-field {
  min-height: 40px;
  font-weight: 700;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  white-space: nowrap;
  min-height: var(--tap-target);
  padding: 0 var(--space-5);
  border: 0;
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  touch-action: manipulation;
}

.btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.btn-touch {
  min-height: var(--tap-target);
  width: 100%;
}

.btn-primary {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
}

.btn-secondary {
  background: var(--color-bg-sunken);
  color: var(--color-text);
}

.btn-danger {
  background: var(--color-danger);
  color: var(--color-danger-contrast);
}

.btn-sm {
  min-height: 40px;
  padding: 0 var(--space-4);
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
}

.form-message {
  color: var(--color-primary);
  font-size: var(--font-size-small);
  font-weight: 600;
}

.form-error {
  color: var(--color-danger);
  font-size: var(--font-size-small);
  font-weight: 600;
}

.form-hint {
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.alert {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: 0.9375rem;
  font-weight: 600;
  margin-bottom: var(--space-3);
}

/* 2026-10-08: a warning is a compact inline row (coloured text, small dot),
   not a big yellow box. A button inside it reads as a text action. */
.alert-warning {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: var(--space-2);
  padding: var(--space-1) 0;
  border-radius: 0;
  background: transparent;
  color: var(--color-warning-text);
  font-size: 0.875rem;
}

.alert-warning::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.alert .btn {
  min-height: 44px;
  padding: 0 var(--space-2);
  background: transparent;
  color: var(--color-primary);
  font-size: 0.875rem;
}

/* Text-only action ("다시 시도", "보기", "+ 장소 추가") -- no grey pill.
   Keeps a 44px hit area through its own height. */
.btn-text {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 44px;
  padding: 0 var(--space-1);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-primary);
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  touch-action: manipulation;
}

.btn-text:disabled {
  color: var(--color-text-faint);
  cursor: default;
}

.btn-text:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* ErrorBoundary.tsx's fallback card -- reuses the danger tokens the
   delete-confirm row (.inline-confirm, further down) already uses, no new
   colour. No border (unlike .alert-warning): --color-danger-soft already
   has enough contrast against --color-bg-elevated without one, and this
   is the one alert that always sits alone inside a whole card rather than
   stacked with other content, so it doesn't need the extra separation a
   border buys .alert-warning. */
.alert-danger {
  background: var(--color-danger-soft);
  color: var(--color-danger);
}

/* ---------- rows inside a card ----------
 * The 시안's main structural idea: short values don't each get a card,
 * they get a row inside one. Separation is a hairline, not a box.
 */

.stat-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) 0;
}

.stat-row:first-child {
  padding-top: 0;
}

.stat-row:last-child {
  padding-bottom: 0;
}

.stat-row + .stat-row {
  border-top: 1px solid var(--color-border);
}

.stat-label {
  /* Never squeezed by a long value, never the thing that wraps: the label
     is the shorter, fixed half of the row. */
  flex: none;
  color: var(--color-text-muted);
  font-size: 0.9375rem;
  font-weight: 600;
}

.stat-value {
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* Only the row variant is right-aligned. `.stat-value` is also used on its
   own (the location card's address), where right-aligning it just looks
   like a mistake.
   `min-width: 0` is load-bearing here: a flex item defaults to
   min-width:auto, so a long value ("잠김 · 문 상태 알 수 없음 · 창문 일부
   열림" -- the security line at its worst) refuses to shrink and pushes the
   row, and the page, wider than the phone. */
.stat-row .stat-value {
  min-width: 0;
  text-align: right;
  overflow-wrap: anywhere;
}

.stat-value.is-missing {
  color: var(--color-text-faint);
  font-weight: 600;
}

/* Timestamps stay — different fields go stale at different times and
   hiding that would be a quiet lie — but they are the faintest thing on
   the screen. */
.stat-caption {
  margin-top: var(--space-2);
  color: var(--color-text-faint);
  font-size: 12px;
  font-weight: 500;
}

.badge {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  background: var(--color-bg-sunken);
  color: var(--color-text-muted);
  font-size: 12px;
  font-weight: 700;
}

.badge-active {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

.location-coordinates {
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  font-size: 0.9375rem;
}

.mini-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3) var(--space-4);
  margin-top: var(--space-4);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 600;
}

.mini-grid strong {
  display: block;
  color: var(--color-text);
  font-size: 1.0625rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.mini-grid + .stat-caption {
  margin-top: var(--space-4);
}

/* Charging month summary: one compact line per charger kind (완속/급속/
   슈퍼차저) under the totals grid -- fits 390px without wrapping the name. */
.kind-totals {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  font-size: var(--font-size-small);
  color: var(--color-text-muted);
}

.kind-totals li {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: 2px 0;
  font-variant-numeric: tabular-nums;
}

.kind-totals-name {
  flex: 0 0 4.5em;
  color: var(--color-text);
  font-weight: 700;
}

/* Actual-vs-estimated 원/kWh comparison + 충전 손실 보정 line (docs/DESIGN.md
   §6) -- flex-basis 100% in the wrapping flex row above forces this onto
   its own line under the kind's count/kWh/원 stats, without a second <li>
   (task point 3's "one compact line per kind" extends to a sub-line, not
   a whole extra row). */
.kind-totals-price {
  flex: 1 0 100%;
  font-size: var(--font-size-small);
  color: var(--color-text-muted);
}

.kind-totals + .stat-caption {
  margin-top: var(--space-3);
}

/* ---------- tables ---------- */

.table-scroll {
  overflow-x: auto;
  margin: 0 calc(var(--space-5) * -1);
  padding: 0 var(--space-5);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.data-table td {
  padding: var(--space-3) var(--space-2);
  border-bottom: 1px solid var(--color-border);
  font-weight: 600;
}

.data-table thead th {
  padding: 0 var(--space-2) var(--space-2);
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
  text-align: left;
  white-space: nowrap;
}

.data-table input[type="text"] {
  width: 100%;
  min-height: 40px;
}

.summary-line {
  color: var(--color-text-muted);
  font-size: 0.9375rem;
  font-weight: 600;
}

/* Responsive "card list" table on phones: each row becomes a block, each
   cell gets its label back via data-label (set in the templates/JSX). */
@media (max-width: 640px) {
  .data-table thead {
    display: none;
  }

  .data-table,
  .data-table tbody,
  .data-table tr,
  .data-table td {
    display: block;
    width: 100%;
  }

  .data-table tr {
    padding: var(--space-4) 0;
  }

  .data-table tr + tr {
    border-top: 1px solid var(--color-border);
  }

  .data-table td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-1) 0;
    border-bottom: none;
  }

  .data-table td[data-label]::before {
    content: attr(data-label);
    color: var(--color-text-muted);
    font-size: var(--font-size-small);
    font-weight: 600;
  }
}

/* ---------- status screen ----------
 * Owner-picked card order: title -> battery (biggest thing on screen) ->
 * location + minimap -> security+climate -> tyres -> odometer/software.
 * See frontend/src/screens/StatusScreen.tsx.
 */

.status-header {
  align-items: flex-end;
}

.status-refresh {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--tap-target);
  margin: 0 calc(var(--space-2) * -1);
  padding: 0 var(--space-2);
  border: none;
  background: transparent;
  color: var(--color-text-faint);
  font-family: inherit;
  font-size: var(--font-size-small);
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
}

.status-refresh:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.status-refresh span[aria-hidden] {
  font-size: 1rem;
  line-height: 1;
}

.status-asof {
  white-space: nowrap;
}

/* Battery: the percentage is the largest text on the whole screen. */
.battery-percent {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.battery-percent-value {
  font-size: 3.75rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}

.battery-percent-value.is-missing {
  font-size: 2rem;
  color: var(--color-text-faint);
}

.battery-row {
  margin: var(--space-4) 0 var(--space-3);
}

.battery-bar {
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-bg-sunken);
  overflow: hidden;
}

.battery-bar.is-missing {
  opacity: 0.5;
}

.battery-bar-fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--color-primary);
}

/* Range and charge state read as one quiet sentence under the number. */
.battery-range {
  color: var(--color-text);
  font-size: 0.9375rem;
  font-weight: 700;
}

.battery-range.is-missing {
  color: var(--color-text-faint);
  font-weight: 600;
}

.charge-state-line {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-text-muted);
  font-size: 0.9375rem;
  font-weight: 600;
}

/* ---------- map ---------- */

.map-view {
  position: relative;
  margin-top: var(--space-4);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-bg-sunken);
}

.map-canvas {
  width: 100%;
  height: 100%;
  min-height: 150px;
}

.status-minimap {
  height: 150px;
}

.map-view-tappable {
  cursor: pointer;
}

.map-view-tappable:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.map-view-hint {
  position: absolute;
  right: var(--space-2);
  bottom: var(--space-2);
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: var(--color-bg-elevated);
  color: var(--color-text-muted);
  font-size: 12px;
  font-weight: 700;
  box-shadow: 0 1px 4px rgba(25, 31, 40, 0.12);
}

.map-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 150px;
  color: var(--color-text-faint);
  font-size: 0.9375rem;
  font-weight: 600;
}

.map-marker {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--color-primary);
  box-shadow: 0 0 0 6px rgba(15, 138, 95, 0.18);
}

.map-marker-car {
  background: var(--color-primary);
}

.map-marker-start {
  background: var(--color-primary);
}

.map-marker-end {
  background: var(--color-danger);
  box-shadow: 0 0 0 6px rgba(229, 72, 77, 0.18);
}

.map-overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  padding-top: env(safe-area-inset-top);
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
}

.map-overlay-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-bg-elevated);
  border-bottom: 1px solid var(--color-border);
}

.map-overlay-body {
  flex: 1;
  min-height: 0;
}

.map-overlay-canvas {
  width: 100%;
  height: 100%;
}

/* ---------- security / climate / tyres ---------- */

.security-line {
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.security-alert-text {
  color: var(--color-danger);
}

.climate-line {
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* Four wheels as a 2x2 grid — the positions matter when one is low. */
.tyre-line {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3) var(--space-5);
}

.tyre-cell {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.tyre-pos {
  width: 30px;
  color: var(--color-text-faint);
  font-size: 12px;
  font-weight: 700;
}

.tyre-line-value {
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.tyre-unit {
  color: var(--color-text-faint);
  font-size: 12px;
  font-weight: 700;
}

/* ---------- month switcher (운행 · 충전) ----------
 * Both month-scoped screens read `?month=YYYY-MM` and, until 2026-09-21,
 * had no way to change it — the owner could only ever see the current
 * month unless they hand-edited the URL. For a 운행기록부 that is not a
 * cosmetic gap: the tax figures the screen exists to produce are per
 * month, and past months are exactly what gets reviewed.
 */

.month-switch {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.month-switch-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--color-bg-elevated);
  color: var(--color-text);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
}

.month-switch-btn:disabled {
  color: var(--color-text-faint);
  cursor: default;
}

.month-switch-btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.month-switch-label {
  min-width: 7.5em;
  text-align: center;
  font-size: 0.9375rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ---------- charging session list ----------
 * A charge session has eight fields; as a table row on a 390px phone they
 * wrap into an unreadable stack. Two lines per session instead: what the
 * owner scans for (when, how much) on the first, the rest as one quiet
 * sentence on the second.
 */

/* One step tighter than the original var(--space-4) 0 (16px) -- the row
   lost a whole 40px control row (the old 메모/경로 보기 button pair) in
   the 2026-09-22 restructure, so the remaining vertical rhythm can afford
   to close up too without feeling cramped. */
.session-row {
  padding: var(--space-3) 0;
}

.session-row:first-child {
  padding-top: 0;
}

.session-row + .session-row {
  border-top: 1px solid var(--color-border);
}

.session-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  /* space-2, not space-3: on a 운행 row the right-hand side now carries
     거리 + .trip-cat-chip + the chevron, and at a 390px viewport the wider
     gap was enough to wrap the when line. 충전 rows share this rule and
     have a shorter right side, so they are unaffected either way. */
  gap: var(--space-2);
}

.session-when {
  font-size: 0.9375rem;
  font-weight: 700;
}

/* .trip-km (TripsScreen's per-row distance) shares this rule so 운행 rows
   match 충전 rows' visual language instead of duplicating the block. */
.session-kwh,
.trip-km {
  font-size: 0.9375rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.session-sub {
  margin-top: 2px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 500;
}

/* Cost that the owner has never filled in is the one thing on this screen
   worth nagging about — it is what makes the month's 경비 total wrong. */
.session-missing-cost {
  color: var(--color-warning-text);
  font-weight: 700;
}

.session-edit {
  margin-top: var(--space-3);
}

/* ---------- trips ---------- */

/* 어디서 → 어디로 (TripsScreen.tsx's tripWhereLine) -- the second thing
   the owner scans after distance, so it's pitched between the loud top
   line (.session-when/.trip-km) and the quiet .session-sub lines below:
   more prominent than .session-sub, not as loud as the top line.
   overflow-wrap keeps a long shortened address (still occasionally two
   Korean words with a hyphenated number, e.g. "증산로17길 6-41") from
   ever forcing the row -- and therefore the page -- wider than the phone.
   This project has already shipped that exact defect twice (see the
   .stat-row .stat-value comment above), so it's treated as a standing
   rule here, not an afterthought. */
.trip-where {
  display: block; /* now a <span> inside .trip-summary-toggle, not a <p> */
  /* 0, down from the shared 2px most other second-lines in this file use
     (see .session-sub) -- the last of a few 1-2px trims made during the
     2026-09-22 (2nd pass) height pass to land the decided/undecided row
     targets (≤70px / ≤115px, measured with a real headless-Chrome
     layout, never estimated -- jsdom doesn't lay anything out). The
     where→where/memo line still reads as its own line without a gap --
     font-weight 600 vs .session-when's 700 keeps the two visually
     distinct on their own. */
  margin-top: 0;
  overflow-wrap: anywhere;
  color: var(--color-text);
  font-size: 0.9375rem;
  font-weight: 600;
}

/* Second half of TripsScreen.tsx's tripWhereLine, tacked onto the same
   .trip-where line after " · " instead of the old standalone "메모: ..."
   <p> (deleted 2026-09-22) -- muted so it doesn't outweigh the
   where→where text ahead of it. A memo the owner actually SAVED
   (row.purpose). */
.trip-memo {
  color: var(--color-text-muted);
  font-weight: 500;
}

/* The other half of tripWhereLine -- row.suggested_purpose, shown only
   when .trip-memo (above) is absent, since a real saved memo always
   wins. Same muted colour as .trip-memo, not the accent: the segmented
   control's .is-suggested cell already carries the "we think" signal for
   this row, so repeating the accent colour here would be a second signal
   for the same fact. */
.trip-memo-hint {
  color: var(--color-text-muted);
  font-weight: 500;
  font-style: italic;
}

/* The row's single tap target (TripsScreen.tsx's TripRowEditor) -- wraps
   ONLY .session-top + .trip-where, never the .seg control below it, which
   stays a sibling outside this button. That split matters on a touch
   screen: a row-wide handler covering the segmented control too would
   race the 개인/업무/출퇴근 taps (this is the same reason the row used to
   carry two separate toggles instead of one). .trip-cat-chip (below) is
   the one exception to "nothing interactive nests here" -- it's a plain
   <span>, not a button, so it's safe inside; a decided row's actual
   control lives in the expanded panel instead (see CategoryChip's own
   doc comment in TripsScreen.tsx). Reset to plain block text, not a
   "button-looking" button -- it must read as part of the row, not a call
   to action. */
.trip-summary-toggle {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
}

.trip-summary-toggle:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.trip-summary-right {
  display: flex;
  align-items: center;
  flex: none;
  gap: var(--space-1);
}

/* Decided-row summary pill (TripsScreen.tsx's CategoryChip) -- everything
   the .seg track (above/below, depending on row.classified) would have
   shown, compressed into the one line the owner is actually scanning
   for. Same colour rule as .seg-option.is-on.is-work: 업무/출퇴근 get the
   accent green via .is-work below, 개인 stays this neutral sunken-chip
   look so a mostly-개인 month doesn't read as entirely green. */
.trip-cat-chip {
  display: inline-flex;
  align-items: center;
  flex: none;
  /* 20px, close to the chip's own natural text-plus-padding height --
     the 2026-09-22 (2nd pass) height pass measured this as the tallest
     element inside .trip-summary-right, so IT alone (not the plain
     text next to it) was setting .session-top's baseline-aligned line
     height on a decided row. Still comfortably legible at 0.8125rem. */
  min-height: 20px;
  padding: 2px var(--space-2);
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
  color: var(--color-text-muted);
  font-size: 0.8125rem;
  font-weight: 700;
  white-space: nowrap;
}

.trip-cat-chip.is-work {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

/* Chevron affordance (inline SVG line icon, same convention as
   components/Nav.tsx's tab icons -- no emoji, see docs/SCREENS.md §0-1).
   Rotates in place on open/close instead of swapping two glyphs. */
.trip-summary-chevron {
  flex: none;
  width: 18px;
  height: 18px;
  color: var(--color-text-faint);
  transition: transform 0.15s ease;
}

.trip-summary-chevron.is-open {
  transform: rotate(180deg);
}

/* One-tap 업무 classification (TripsScreen.tsx's ClassifySegment) -- an
   iOS-style segmented pill: a sunken track holding three equal cells,
   the selected cell "cut out" onto the card's own elevated surface so it
   reads as chosen the same way a raised iOS segment thumb does. Built
   entirely from existing tokens (no new colours), so the dark-mode
   :root override block up top -- which just swaps --color-bg-sunken /
   --color-bg-elevated to darker values -- already covers this with zero
   extra rules here.

   2026-09-22 (2nd pass): this track used to sit under EVERY row,
   including ones the owner had already classified and was only scanning
   past -- 44% of a collapsed row's height spent on a control most taps
   never touched. It now renders only on an UNDECIDED row (see
   TripRowEditor's doc comment); a decided row shows CategoryChip in the
   summary instead and this track never appears there at all. That
   changes what the sunken background MEANS, not just how often it shows
   up: it used to be pure chrome, now it's a signal -- "this row still
   needs you" -- so keeping it (rather than flattening it further) is
   worth the space it costs on the rows where it does appear. margin-top
   drops a step (space-3 -> space-2) as the other half of buying back
   height on those rows. */
.seg {
  display: flex;
  gap: 2px;
  margin-top: var(--space-2);
  padding: 2px;
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
}

.seg-option {
  flex: 1 1 0;
  min-width: 0; /* letters must shrink before the pill forces the row wider */
  /* 34px, down from the 2026-09-22 (1st pass) value of 40px. That pass
     pinned 40px to .btn-sm's floor with the reasoning "a secondary
     control never goes below it" -- but .btn-sm is a free-standing
     button competing for thumb accuracy with whatever sits next to it,
     while .seg-option (now that it only appears on an undecided row,
     see .seg's comment above) is the ONLY tap target across the full
     width of its own row: three large touch zones with nothing to
     mis-hit into. That's a different accuracy budget, so it can safely
     spend less of it on height. */
  min-height: 34px;
  padding: 0 var(--space-2);
  border: 0;
  border-radius: calc(var(--radius-md) - 2px);
  background: transparent;
  color: var(--color-text-muted);
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
}

.seg-option:disabled {
  opacity: 0.6;
  cursor: default;
}

.seg-option:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Selected, but NOT a work category (개인) -- neutral dark text on the
   card's own surface, deliberately never green. 개인 is the default for
   most trips (most driving isn't for work), so colouring it green too
   would turn the whole month's list green and destroy the one scan the
   owner actually performs on this screen: "which trips have I marked as
   work". Only .is-on.is-work (below) gets the accent colour. */
.seg-option.is-on {
  background: var(--color-bg-elevated);
  color: var(--color-text);
  box-shadow: var(--shadow-card);
}

/* Selected AND a work category (업무/출퇴근 -- see TripsScreen.tsx's
   isWorkCategory helper, which this class name mirrors). The one place
   on this row allowed to use the accent green, because it's the one
   piece of information the owner is actually scanning for. */
.seg-option.is-on.is-work {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

/* "We think this one" (TripsScreen.tsx's ClassifySegment, marking the
   cell matching row.suggested_category -- trip_suggestions.py's guess
   from a similar past trip). A DASHED outline on the ordinary
   transparent track, never the solid raised .is-on fill above -- it must
   read as a hint, not as an already-made choice, because it isn't one:
   the server never applies a suggestion (업무 자동 확정 금지, this
   project's CLAUDE.md), it only becomes real once the owner taps the
   cell. In practice this is never paired with .is-on: a suggestion only
   ever attaches to an undecided row, whose category still defaults to
   개인, so the suggested cell and the selected cell are never the same
   cell. */
.seg-option.is-suggested {
  outline: 1px dashed var(--color-primary);
  outline-offset: -1px;
  color: var(--color-primary);
}

/* The needs_review flag is the one thing on this line worth nagging about
   -- same "warning colour on the part that needs attention" treatment as
   .session-missing-cost above. */
.session-sub strong {
  color: var(--color-warning-text);
}

/* "Reuse last time's memo" chip in the expanded panel's memo form
   (TripsScreen.tsx's TripRowEditor) -- appears only while the input is
   still empty and row.suggested_purpose has something to offer. Same
   dashed/no-fill/accent-text language as .seg-option.is-suggested above
   so the two "we remember this" cues on the same row read as one system,
   not two unrelated ones. */
.trip-memo-chip {
  display: inline-flex;
  align-self: flex-start;
  align-items: center;
  min-height: 34px;
  padding: 0 var(--space-2);
  border: 1px dashed var(--color-primary);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-primary);
  font-family: inherit;
  font-size: var(--font-size-small);
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
}

.trip-memo-chip:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.trip-route-map {
  height: 220px;
}

/* Near-zero (parking-lot shuffle) trips, hidden by default -- see
   TripsScreen.tsx's SHORT_TRIP_KM. Styled as a quiet continuation of the
   row list (same top border a third .session-row would get), not a card
   or an alert -- it's a filter state, not something wrong. */
.trip-short-toggle-row {
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.trip-short-toggle {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-muted);
  font-family: inherit;
  font-size: var(--font-size-small);
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
}

.trip-short-toggle:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* 외출(outing) group header (TripsScreen.tsx's OutingHeader) -- drawn above
   the first VISIBLE row of a multi-trip outing when the owner's 외출
   묶어서 보기 switch is on. A quiet sunken block (same surface language as
   .seg's track) rather than a card-in-a-card, since it sits inside the
   trips list's own .card. Column layout (info, then the 전부 업무 action
   below it) rather than a single row with space-between: InlineConfirm's
   expanded confirm state is itself a wrapping flex row (.inline-confirm),
   and nesting that inside a space-between row produced an unpredictable
   width fight at 390px -- stacking keeps both states simple. */
.outing-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  margin: var(--space-2) 0;
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
}

.outing-header-info {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}

.outing-header-when {
  font-size: 0.875rem;
  font-weight: 700;
}

.outing-header-meta {
  flex: none;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 600;
  white-space: nowrap;
}

/* "자동·구산 포덕소" marker (TripsScreen.tsx, TripRow.classified_by_rule) --
   same quiet .session-sub language as the is_gap/needs_review line right
   above it on the row, so a rule-applied classification reads as
   informational, not a warning (unlike needs_review, which stays bold/
   warning-coloured). */
.trip-rule-marker {
  font-style: italic;
}

/* ---------- shared widgets ---------- */

.year-xlsx-download {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.year-xlsx-download .input-field {
  min-height: 40px;
  flex: none;
}

.inline-confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-danger-soft);
}

.inline-confirm-message {
  flex-basis: 100%;
  color: var(--color-danger);
  font-size: var(--font-size-small);
  font-weight: 600;
  line-height: 1.5;
}

.inline-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* ---------- places / afterblow / save-location ---------- */

.place-list {
  display: flex;
  flex-direction: column;
}

.place-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) 0;
}

/* Collapsed by default: a place is read far more often than it is edited,
   and five unlabelled number boxes per place (what this looked like before
   2026-09-21) is not a screen anyone can read. */
.place-name {
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* Wraps .place-name + the optional 「항상 업무」/「항상 출퇴근」 .badge
   (PlacesSection.tsx) -- a real flex gap between them instead of
   concatenating the badge straight onto the name text, and flex-wrap so a
   long name + badge together wrap onto two lines rather than overflow a
   390px phone. */
.place-name-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.place-meta {
  /* A <span>, so this has to be said out loud -- without it the name and
     the radius run together as "집반경 100m". */
  display: block;
  margin-top: 2px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 500;
}

.place-edit {
  padding-bottom: var(--space-4);
}

.place-row:first-child {
  padding-top: 0;
}

.place-row + .place-row {
  border-top: 1px solid var(--color-border);
}

.new-place-form {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.new-place-form h3 {
  margin-bottom: var(--space-3);
}

.afterblow-settings-form label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  font-size: 0.9375rem;
  font-weight: 600;
}

.afterblow-settings-form label + label {
  border-top: 1px solid var(--color-border);
}

.afterblow-settings-form .input-field {
  width: 96px;
  min-height: 40px;
  text-align: right;
  font-weight: 700;
}

.afterblow-settings-form .afterblow-toggle {
  align-items: flex-start;
  gap: var(--space-4);
  font-weight: 700;
}

.afterblow-run-now {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.settings-fieldset {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.settings-toggle-row,
.settings-fieldset .field-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  font-size: 0.9375rem;
  font-weight: 600;
}

.settings-toggle-row + .settings-toggle-row,
.settings-toggle-row + .field-label,
.settings-fieldset .field-label + .settings-toggle-row {
  border-top: 1px solid var(--color-border);
}

.settings-toggle-row input[type='checkbox'] {
  width: 22px;
  height: 22px;
  flex: none;
  accent-color: var(--color-primary);
}

.settings-fieldset .field-label .input-field {
  width: auto;
  min-width: 132px;
}

.settings-actions,
.notification-history {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.notification-history .session-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.section-disclosure {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--color-text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.section-disclosure span:first-child {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}

.section-disclosure small {
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.section-disclosure-body,
.odometer-form,
.odometer-list {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.year-picker {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-bottom: var(--space-3);
}

.odometer-form label {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  font-size: var(--font-size-small);
  font-weight: 600;
}

.odometer-form .input-field,
.odometer-form input[type='file'] {
  width: 100%;
  box-sizing: border-box;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.collection-warning {
  margin-top: var(--space-3);
}

.usage-summary {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

.usage-summary h3,
.odometer-list h3 {
  margin-bottom: var(--space-3);
  font-size: 1rem;
}

.save-location-form {
  margin-top: var(--space-4);
}

.settings-toggle-list,
.integration-stack {
  display: grid;
  gap: 0;
  margin-top: var(--space-3);
}

.settings-toggle-row > span,
.compact-list li > span {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}

.settings-toggle-row small,
.compact-list small {
  color: var(--color-text-muted);
  font-weight: 400;
}

.compact-list,
.timeline-list,
.stage-list {
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
}

.compact-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--color-border);
}

.compact-list .is-superseded { opacity: .65; }
.session-row.is-focused { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: var(--radius-sm); }
.chip-list { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-3) 0; }
.status-chip { padding: 4px 8px; border-radius: 999px; background: var(--color-surface-muted); font-size: var(--font-size-small); }

.timeline-list { border-left: 2px solid var(--color-border); margin-left: var(--space-2); padding-left: var(--space-4); }
.timeline-list li { position: relative; padding: 0 0 var(--space-4); }
.timeline-list li::before { content: ''; position: absolute; left: calc(-1 * var(--space-4) - 6px); top: 5px; width: 10px; height: 10px; border-radius: 50%; background: var(--color-accent); }
.timeline-list li > small { display: block; color: var(--color-text-muted); }
.timeline-list p { margin: var(--space-1) 0 0; }

.table-scroll { max-width: 100%; overflow-x: auto; }
.compact-table { width: 100%; border-collapse: collapse; font-size: var(--font-size-small); }
.compact-table th,.compact-table td { padding: var(--space-2); border-bottom: 1px solid var(--color-border); text-align: left; vertical-align: top; }
.compact-table th { white-space: nowrap; }

.compact-form { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: var(--space-3); margin: var(--space-4) 0; }
.compact-form h3,.compact-form fieldset,.compact-form .form-actions,.compact-form .alert,.compact-form .preview-box { grid-column: 1/-1; }
.compact-form label { display: grid; gap: var(--space-1); font-size: var(--font-size-small); font-weight: 600; min-width: 0; }
.compact-form textarea { min-height: 84px; resize: vertical; }
.weekday-grid { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.preview-box { padding: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface-muted); }

.afterblow-diagnostics { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--color-border); }
.condition-grid { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: var(--space-2); }
.condition-item { display: flex; justify-content: space-between; gap: var(--space-2); padding: var(--space-2); border-radius: var(--radius-sm); background: var(--color-surface-muted); font-size: var(--font-size-small); }
.condition-item.is-pass { color: var(--color-success-text); }
.condition-item.is-fail { color: var(--color-danger); }
.condition-item.is-unknown { color: var(--color-text-muted); }
.stage-list li { padding: var(--space-1) 0; color: var(--color-text-muted); }
.stage-list li.is-done { color: var(--color-success-text); }

@media (max-width: 480px) {
  .compact-form { grid-template-columns: 1fr; }
  .compact-form > * { grid-column: 1; }
  .condition-grid { grid-template-columns: 1fr; }
  .compact-list li { align-items: flex-start; }
}

/* A settings group gets a quiet label above its card, like the 시안. */
.group-title {
  margin: var(--space-6) 0 var(--space-2) var(--space-2);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
}

/* ---------- Desktop: wider, multi-column ---------- */

@media (min-width: 1024px) {
  :root {
    --max-content-width: 960px;
  }

  .content {
    padding: var(--space-7) var(--space-6);
  }

  .card-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-4);
  }

  /* Header comes back and carries the tabs; bottom bar goes away. */
  .app-header-app {
    display: flex;
  }

  .main-nav {
    display: flex;
  }

  .bottom-tab-bar {
    display: none;
  }
}

/* ---------- Car: landscape, touch-only, bigger everything ----------
 * Auto-detected via pointer/orientation/width/height, never User-Agent
 * (Tesla's in-car browser does not reliably identify itself in the UA
 * string). See the file header for why min-height is in this query. */
@media (pointer: coarse) and (orientation: landscape) and (min-width: 900px) and (min-height: 600px) {
  :root {
    --font-size-body: 20px;
    --tap-target: 56px;
    --space-3: 16px;
    --space-4: 20px;
    --max-content-width: 1100px;
  }

  .app-header .brand {
    font-size: 1.25rem;
  }

  .app-header-app {
    display: flex;
  }

  .main-nav {
    display: flex;
  }

  .main-nav .tab-link {
    min-height: 52px;
    font-size: 1.0625rem;
  }

  .bottom-tab-bar {
    display: none;
  }

  .content {
    padding-bottom: var(--space-6);
  }

  .battery-percent-value {
    font-size: 4.75rem;
  }

  .btn {
    font-size: 1.0625rem;
  }
}

/* Explicit override via ?layout=car / ?layout=standard (see base.html),
   remembered in localStorage. Wins over the automatic media query above
   because it is applied later in the cascade with an attribute selector
   scoped to :root, same specificity family but declared after it. */
html[data-layout="car"] {
  --font-size-body: 20px;
  --tap-target: 56px;
  --space-3: 16px;
  --space-4: 20px;
  --max-content-width: 1100px;
}

/* Unconditional (not nested in a width media query) so this genuinely
   forces the car top-row layout even outside the auto-detect media
   query's own conditions. Scoped with the attribute-selector + descendant
   combinator (not a bare `.main-nav`/`.content`) specifically so its
   specificity beats the `@media (min-width: 1024px)` block regardless of
   which one is later in the file. */
html[data-layout="car"] .app-header-app,
html[data-layout="car"] .main-nav {
  display: flex;
}

html[data-layout="car"] .bottom-tab-bar {
  display: none;
}

html[data-layout="car"] .content {
  padding-bottom: var(--space-6);
}

html[data-layout="car"] .battery-percent-value {
  font-size: 4.75rem;
}

html[data-layout="car"] .status-minimap {
  height: 210px;
}

html[data-layout="car"] .trip-route-map {
  height: 300px;
}

html[data-layout="standard"] {
  --font-size-body: 16px;
  --tap-target: 48px;
  --space-3: 12px;
  --space-4: 16px;
}

/* Forces the bottom tab bar even on a coarse-pointer/landscape/wide device
   that would otherwise auto-detect as car (the documented purpose of this
   override) -- same unconditional-override + specificity reasoning as
   html[data-layout="car"] above. */
html[data-layout="standard"] .app-header-app,
html[data-layout="standard"] .main-nav {
  display: none;
}

html[data-layout="standard"] .bottom-tab-bar {
  display: flex;
}

html[data-layout="standard"] .content {
  padding-bottom: calc(var(--bottom-nav-height) + env(safe-area-inset-bottom) + var(--space-6));
}

/* ---------- 운행 기간 요약 (TripPeriodSummary.tsx, 2026-10-08 재설계) ----------
 * 테슬라 앱처럼 평평하게: 카드 상자 없이 페이지 위에 바로 놓고, 묶음은 간격으로만
 * 나눈다(근접성). 강조는 하나 -- 「N만원 아꼈어요」만 초록, 가장 큰 글자다
 * (폰 레스토프). 나머지 숫자는 조용한 세 칸과 「자세히」 뒤로 보낸다(힉).
 * 버튼은 전부 --tap-target(48px, 차량 56px) 이상이고 hover에 기대지 않는다.
 * 글자 크기는 --font-size-body 기준으로 계산해 차량 레이아웃(20px)에서 같이 커진다.
 * 움직임은 두 가지뿐(자세히 화살표 회전, 펼칠 때 살짝 나타남)이고
 * prefers-reduced-motion에서는 둘 다 끈다(이 절 끝). */
.trip-summary {
  /* Label and value stay within one glance on desktop / the car screen
     instead of drifting 1000px apart. */
  max-width: 40rem;
  margin-bottom: var(--space-7);
  /* Korean wraps at word boundaries, never mid-word ("계산했어 / 요"). */
  word-break: keep-all;
  overflow-wrap: anywhere;
}

/* 글자 탭 -- 회색 판(.seg) 대신 글자만, 고른 탭은 진한 글자 + 밑줄. */
.period-text-tabs {
  display: flex;
  gap: var(--space-1);
  margin-left: calc(var(--space-3) * -1);
}

.period-text-tab {
  position: relative;
  min-width: var(--tap-target);
  min-height: var(--tap-target);
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-muted);
  font-family: inherit;
  font-size: calc(var(--font-size-body) * 0.9375);
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
}

.period-text-tab.is-on {
  color: var(--color-text);
  font-weight: 800;
}

.period-text-tab.is-on::after {
  content: "";
  position: absolute;
  left: var(--space-3);
  right: var(--space-3);
  bottom: 8px;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
}

.period-text-tab:focus-visible,
.period-nav-btn:focus-visible,
.summary-details-toggle:focus-visible,
.detail-link:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.period-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--tap-target);
  margin: var(--space-1) calc(var(--space-3) * -1) 0;
}

.period-nav-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--tap-target);
  height: var(--tap-target);
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  touch-action: manipulation;
}

.period-nav-btn:disabled {
  color: var(--color-text-faint);
  cursor: default;
}

.period-nav-label {
  flex: 1;
  text-align: center;
  font-size: calc(var(--font-size-body) * 1.0625);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Same footprint whether it holds the numbers or the first-load skeleton,
   so the list under it never jumps. The previous period stays visible,
   dimmed, while the next one loads. */
.trip-summary-body {
  min-height: calc(var(--font-size-body) * 16);
  transition: opacity 0.15s ease;
}

.trip-summary-body.is-busy {
  opacity: 0.45;
}

.summary-hero {
  margin-top: var(--space-4);
}

.summary-hero-label {
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.9375);
  font-weight: 600;
}

.summary-hero-value {
  margin-top: 2px;
  color: var(--color-primary);
  font-size: calc(var(--font-size-body) * 2);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.summary-hero-value.is-neutral {
  color: var(--color-text);
}

.summary-hero-note {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.8125);
}

/* The one picture: 휘발유 (neutral, full) vs 전기 (green, proportional).
   No track -- on the flat page a track colour would vanish into the
   background anyway; the empty space IS the difference. */
.compare-bars {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.compare-row {
  display: grid;
  grid-template-columns: 3em 1fr auto;
  align-items: center;
  gap: var(--space-3);
}

.compare-name {
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.8125);
  font-weight: 600;
}

.compare-track {
  display: block;
  height: 10px;
}

.compare-fill {
  display: block;
  height: 100%;
  min-width: 4px;
  border-radius: var(--radius-pill);
  background: var(--color-text-faint);
}

.compare-fill.is-ev {
  background: var(--color-primary);
}

.compare-value {
  min-width: 4.5em;
  text-align: right;
  font-size: calc(var(--font-size-body) * 0.9375);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Quiet three-column strip: 주행 / 전비 / 전력. */
.summary-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  margin: var(--space-5) 0 0;
}

.summary-strip dt {
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.8125);
  font-weight: 600;
}

.summary-strip dd {
  margin: 2px 0 0;
  font-size: calc(var(--font-size-body) * 1.125);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.summary-strip dd small {
  color: var(--color-text-muted);
  font-size: 0.7em;
  font-weight: 600;
}

.summary-strip-note {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.75);
}

.trip-summary-empty {
  padding: var(--space-6) 0;
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.9375);
  text-align: center;
}

.trip-summary-error {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-3);
  color: var(--color-warning-text);
  font-size: calc(var(--font-size-body) * 0.9375);
  font-weight: 600;
}

/* On the flat page the sunken secondary button would vanish -- lift it. */
.trip-summary-error .btn {
  flex: none;
  background: var(--color-bg-elevated);
}

/* Small 「자세히 보기」 row: quiet text, but the whole row is the tap target. */
.summary-details-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  width: 100%;
  min-height: var(--tap-target);
  margin-top: var(--space-3);
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-muted);
  font-family: inherit;
  font-size: calc(var(--font-size-body) * 0.875);
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
}

.summary-details-chevron {
  width: 16px;
  height: 16px;
  transition: transform 0.15s ease;
}

.summary-details-chevron.is-open {
  transform: rotate(180deg);
}

.summary-details {
  animation: summary-details-in 0.15s ease-out;
}

@keyframes summary-details-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* Plain list rows, grouped by spacing (no boxes, no rules). */
.detail-group + .detail-group {
  margin-top: var(--space-5);
}

.detail-group-title {
  margin-bottom: var(--space-1);
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.8125);
  font-weight: 700;
  letter-spacing: 0;
}

.detail-list {
  margin: 0;
}

.detail-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 6px 0;
  font-size: calc(var(--font-size-body) * 0.9375);
}

.detail-row dt {
  flex: none;
  color: var(--color-text);
  font-weight: 500;
}

.detail-row dd {
  min-width: 0;
  margin: 0;
  text-align: right;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.detail-row dd strong {
  font-weight: 800;
}

.detail-sub {
  margin-right: var(--space-3);
  color: var(--color-text-muted);
  font-weight: 500;
}

.detail-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 0 6px;
  border: 1px solid var(--color-text-faint);
  border-radius: var(--radius-pill);
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.6875);
  font-weight: 700;
  line-height: 16px;
  vertical-align: 2px;
}

.detail-note {
  margin-top: 2px;
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.75);
}

.detail-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target);
  border-radius: var(--radius-sm);
  color: var(--color-text);
  font-size: calc(var(--font-size-body) * 0.875);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* First-load skeleton -- same blocks as hero + bars + strip. A neutral
   translucent grey so it reads on both the light and the dark page. */
.trip-summary-skeleton {
  display: flex;
  flex-direction: column;
}

.skeleton-line {
  display: block;
  border-radius: var(--radius-sm);
  background: rgba(127, 127, 127, 0.14);
}

.skeleton-line.is-label {
  width: 30%;
  height: 14px;
  margin-top: var(--space-4);
}

.skeleton-line.is-hero {
  width: 70%;
  height: 34px;
  margin-top: var(--space-2);
}

.skeleton-line.is-bar {
  width: 100%;
  height: 10px;
  margin-top: var(--space-3);
}

.trip-summary-skeleton .summary-strip .skeleton-line {
  height: 40px;
}

/* Reserves the 「자세히 보기」 row so the skeleton is as tall as the result. */
.skeleton-toggle {
  display: block;
  height: var(--tap-target);
  margin-top: var(--space-3);
}

/* ---------- 운행 줄 오른쪽 열 (금액과 전력, 거래 내역처럼) ---------- */
.trip-summary-second {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
}

.trip-summary-second .trip-where {
  flex: 1;
  min-width: 0;
}

.trip-energy {
  display: flex;
  flex: none;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.trip-energy-cost {
  color: var(--color-text);
  font-size: 0.875rem;
  font-weight: 600;
}

.trip-energy-kwh {
  color: var(--color-text-muted);
  font-size: 12px;
  font-weight: 500;
}

/* ---------- 설정 「요금 기준」 (RateSettingsSection.tsx) ---------- */
.rate-group + .rate-group {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
}

.rate-group-title {
  margin-bottom: var(--space-3);
  font-size: 0.9375rem;
  font-weight: 800;
}

.rate-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}

.rate-field label {
  display: block;
  margin-bottom: 6px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 600;
}

.unit-input {
  position: relative;
}

.unit-input .input-field {
  width: 100%;
  padding-right: 4.25em;
  font-variant-numeric: tabular-nums;
}

.unit-input-suffix {
  position: absolute;
  top: 50%;
  right: var(--space-3);
  transform: translateY(-50%);
  color: var(--color-text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  pointer-events: none;
}

.rate-group .form-hint {
  margin-top: var(--space-2);
  word-break: keep-all;
}

.rate-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.rate-footer p {
  flex: 1;
}

@media (prefers-reduced-motion: reduce) {
  .summary-details-chevron,
  .trip-summary-chevron,
  .trip-summary-body {
    transition: none;
  }

  .summary-details {
    animation: none;
  }
}

/* ---------- flat list rows (2026-10-08, all screens) ----------
 * One row = one fact: label (with an optional line icon) on the left,
 * value on the right, hairline between rows. A row that opens something is
 * itself the button (.list-row-button) with a chevron -- no separate grey
 * "수정/보기" pill. */
.list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 52px;
  padding: var(--space-2) 0;
}

.list-row + .list-row,
.list-row + .list-row-group,
.list-row-group + .list-row,
.list-row-group + .list-row-group {
  border-top: 1px solid var(--color-border);
}

.list-row-label {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  color: var(--color-text);
  font-size: 0.9375rem;
  font-weight: 500;
}

.list-row-label > span {
  min-width: 0;
}

.list-row-sub {
  display: block;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 500;
}

.list-row-icon {
  flex: none;
  width: 20px;
  height: 20px;
  color: var(--color-text-muted);
}

.list-row-value {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  min-width: 0;
  text-align: right;
  font-size: 0.9375rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.list-row-value.is-alert {
  color: var(--color-danger);
}

.list-row-value.is-muted {
  color: var(--color-text-muted);
  font-weight: 500;
}

.list-row-stale {
  color: var(--color-text-faint);
  font-size: 12px;
  font-weight: 500;
}

.list-row-button {
  width: 100%;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
}

.list-row-button:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
  border-radius: var(--radius-sm);
}

.list-row-end {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex: none;
  min-width: 0;
}

.list-row-chevron {
  flex: none;
  width: 18px;
  height: 18px;
  color: var(--color-text-faint);
  transition: transform 0.15s ease;
}

.list-row-chevron.is-open {
  transform: rotate(180deg);
}

.list-row-panel {
  padding: 0 0 var(--space-4);
}

.list-row-add {
  color: var(--color-primary);
  font-weight: 700;
}

.section-footer {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
  font-size: 12px;
  word-break: keep-all;
}

/* iOS-style switch for on/off settings rows. Same <input type=checkbox>
   underneath (keyboard, screen readers and tests unchanged). */
.settings-toggle-row input[type="checkbox"],
.stat-row input[type="checkbox"],
.list-row input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  flex: none;
  width: 44px;
  height: 26px;
  margin: 9px 0;
  border-radius: var(--radius-pill);
  background: var(--color-text-faint);
  cursor: pointer;
  transition: background 0.15s ease;
}

.settings-toggle-row input[type="checkbox"]::after,
.stat-row input[type="checkbox"]::after,
.list-row input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #ffffff;
  transition: transform 0.15s ease;
}

.settings-toggle-row input[type="checkbox"]:checked,
.stat-row input[type="checkbox"]:checked,
.list-row input[type="checkbox"]:checked {
  background: var(--color-primary);
}

.settings-toggle-row input[type="checkbox"]:checked::after,
.stat-row input[type="checkbox"]:checked::after,
.list-row input[type="checkbox"]:checked::after {
  transform: translateX(18px);
}

.settings-toggle-row input[type="checkbox"]:disabled,
.stat-row input[type="checkbox"]:disabled,
.list-row input[type="checkbox"]:disabled {
  opacity: 0.5;
  cursor: default;
}

.settings-toggle-row input[type="checkbox"]:focus-visible,
.stat-row input[type="checkbox"]:focus-visible,
.list-row input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .list-row-chevron,
  .settings-toggle-row input[type="checkbox"],
  .settings-toggle-row input[type="checkbox"]::after,
  .stat-row input[type="checkbox"]::after,
  .list-row input[type="checkbox"]::after {
    transition: none;
  }
}

/* ---------- 운행 목록 (2026-10-08 flat pass) ---------- */
/* Where/memo line: ONE line, ellipsis -- never a 2-line wrap like
   「가족 나들 / 이」. The full addresses and memo are in the expanded row. */
.trip-summary-second .trip-where,
.trip-where {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow-wrap: normal;
}

/* Undecided-row 개인/업무/출퇴근: 36px visual track, 44px hit area (the
   ::after extends each cell's tap zone 6px up and down, into the row's
   own spacing -- nothing else interactive lives there). */
.seg {
  margin-top: var(--space-2);
  margin-bottom: 6px;
}

.seg-option {
  position: relative;
  min-height: 32px;
  font-size: 0.8125rem;
}

.seg-option::after {
  content: "";
  position: absolute;
  top: -6px;
  bottom: -6px;
  left: 0;
  right: 0;
}

.trip-list .card-title + .session-row,
.trip-list .card-title + .outing-header {
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-3);
}

/* 외출 header: a quiet label line, not a grey box. */
.outing-header {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0 var(--space-2);
  margin: 0;
  padding: var(--space-2) 0 0;
  border-top: 1px solid var(--color-border);
  border-radius: 0;
  background: transparent;
}

.outing-header .outing-header-info {
  flex: 1;
  min-width: 0;
  justify-content: flex-start;
}

.outing-header + .session-row {
  border-top: 0;
}

/* 계기판 사진 대조 + 연간 엑셀: plain rows at the bottom. */
.trip-tools .odometer-section + .list-row {
  border-top: 1px solid var(--color-border);
}

.year-xlsx-download {
  display: flex;
}

.year-xlsx-select.input-field {
  min-height: 36px;
  padding: 0 var(--space-2);
  font-size: 0.875rem;
}

/* ---------- 상태 (2026-10-08 flat pass) ---------- */
.status-hero {
  margin-bottom: var(--space-7);
}

.status-battery {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--space-3);
}

.status-range {
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 1.375);
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.status-hero .battery-bar {
  height: 6px;
  margin: var(--space-3) 0;
}

.status-sentence {
  font-size: calc(var(--font-size-body) * 1.0625);
  font-weight: 600;
  word-break: keep-all;
}

.status-sentence.is-alert {
  color: var(--color-danger);
}

.status-sentence-sub {
  margin-top: 2px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.status-place .list-row-label {
  font-weight: 700;
}

.status-place-name {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.status-location .map-view {
  margin-top: 0;
}

.status-minimap {
  height: 180px;
}

.status-location .status-save-place {
  margin-top: var(--space-1);
}

.status-rows .list-row-value {
  max-width: 62%;
}

.list-row-group .tyre-line {
  padding-left: calc(20px + var(--space-3));
}

.nowrap {
  white-space: nowrap;
}

.status-rows .list-row-button .list-row-value {
  max-width: none;
}

/* ---------- 충전 (2026-10-08 flat pass) ---------- */
.page-header-with-nav {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-1);
}

.page-header-with-nav .period-nav {
  margin: 0 calc(var(--space-3) * -1);
}

@media (min-width: 1024px) {
  .page-header-with-nav {
    flex-direction: row;
    align-items: center;
  }

  .page-header-with-nav .period-nav {
    margin: 0;
    min-width: 16em;
  }
}

.charge-hero {
  margin-bottom: var(--space-6);
}

.charge-hero-value {
  margin-top: 2px;
  font-size: calc(var(--font-size-body) * 2);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.charge-hero-sub {
  margin-top: var(--space-1);
  color: var(--color-text);
  font-size: calc(var(--font-size-body) * 0.9375);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.charge-hero-note {
  margin-top: 2px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.charge-details {
  border-top: 1px solid var(--color-border);
}

.detail-row-stack {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}

.detail-row-stack dd {
  text-align: left;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 500;
}

.charge-list .card-title + .session-row {
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-3);
}

.charge-row-toggle {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.charge-row-main {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
}

.charge-row-main .session-sub {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.charge-row-aside {
  display: flex;
  flex: none;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.charge-row-cost {
  font-size: 0.9375rem;
  font-weight: 800;
}

.charge-row-cost .detail-tag {
  margin: 0 6px 0 0;
}

.charge-row-toggle .list-row-chevron {
  margin-top: 2px;
}

.unit-input {
  display: block;
}

.stack-form label .unit-input {
  flex: 1;
  max-width: 14em;
}

/* Small secondary buttons are row-level actions ("정정", "삭제 취소",
   "이력 보기"...): text, not a grey pill (2026-10-08). The 44px hit area
   stays. Primary small buttons (저장 etc.) keep their fill. */
.btn-sm.btn-secondary {
  min-height: 44px;
  padding: 0 var(--space-2);
  background: transparent;
  color: var(--color-primary);
}

.btn-sm.btn-secondary:disabled {
  color: var(--color-text-faint);
}

/* ---------- 설정 (2026-10-08 grouped-list pass) ---------- */
.group-title {
  margin: var(--space-6) 0 var(--space-1);
}

.place-row {
  display: block;
  padding: 0;
}

.place-row + .place-row {
  border-top: 1px solid var(--color-border);
}

.place-row .place-name {
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0;
}

.place-row .place-edit {
  padding: 0 0 var(--space-4);
}

.new-place-form {
  margin-top: 0;
  padding-top: 0;
  border-top: 1px solid var(--color-border);
}

.new-place-form.stack-form {
  padding-top: var(--space-3);
}

.rate-fields {
  display: block;
}

.rate-group + .rate-group {
  margin-top: var(--space-5);
  padding-top: 0;
  border-top: 0;
}

.rate-group-title {
  margin-bottom: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.rate-field label {
  display: inline;
  margin: 0;
  color: var(--color-text);
  font-size: 0.9375rem;
  font-weight: 500;
}

.rate-input {
  flex: none;
  width: 9.5em;
}

.rate-input .input-field {
  min-height: 40px;
  text-align: right;
  padding-right: 4.5em;
}

.settings-toggle-row,
.settings-fieldset .field-label {
  min-height: 52px;
  padding: var(--space-1) 0;
  font-weight: 500;
}

.settings-actions,
.notification-history,
.usage-summary {
  margin-top: 0;
  padding-top: 0;
  border-top: 1px solid var(--color-border);
}

.usage-summary {
  margin-top: var(--space-5);
  border-top: 0;
}

.settings-actions + .list-row {
  border-top: 1px solid var(--color-border);
}

.list-row-button:disabled {
  cursor: default;
  opacity: 0.6;
}

.section-disclosure {
  min-height: 52px;
}

.place-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 선택 연동 / 정비 / 요금표: <details> rows look like the other list rows. */
.integration-stack details + details {
  border-top: 1px solid var(--color-border);
}

details > summary.section-disclosure {
  display: flex;
  min-height: 52px;
  padding: var(--space-2) 0;
  list-style: none;
}

details > summary.section-disclosure::-webkit-details-marker {
  display: none;
}

details > summary.section-disclosure::after {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  margin-right: 6px;
  border-right: 1.8px solid var(--color-text-faint);
  border-bottom: 1.8px solid var(--color-text-faint);
  transform: rotate(45deg);
}

details[open] > summary.section-disclosure::after {
  transform: rotate(-135deg);
}

.section-disclosure strong {
  font-weight: 500;
}

.afterblow-settings-form.stack-form {
  gap: 0;
}

.afterblow-settings-form .rate-field span:first-child {
  font-size: 0.9375rem;
  font-weight: 500;
}

.afterblow-history-title {
  margin-top: var(--space-5);
}

.stack-form label .rate-input,
.rate-input {
  flex: none;
  width: 9em;
  max-width: none;
}

.rate-input .input-field {
  width: 100%;
}

.afterblow-diagnostics h3 {
  margin-bottom: var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.afterblow-diagnostics .text-muted,
.afterblow-diagnostics p {
  font-size: var(--font-size-small);
}

.condition-grid strong,
.condition-grid b {
  font-weight: 500;
}

.usage-summary h3.card-title {
  margin-bottom: var(--space-1);
  font-size: var(--font-size-small);
}

.list-row-add .list-row-label {
  color: var(--color-primary);
  font-weight: 700;
}

/* Flat rows need label and value within one glance: a single readable
   column on desktop and in the car instead of 1000px-wide rows
   (2026-10-08). In the car, rem-sized text scales with the 20px body too. */
@media (min-width: 1024px) {
  :root {
    --max-content-width: 760px;
  }
}

@media (pointer: coarse) and (orientation: landscape) and (min-width: 900px) and (min-height: 600px) {
  :root {
    --max-content-width: 880px;
    --font-size-small: 16px;
    font-size: 20px;
  }
}

html[data-layout="car"] {
  --max-content-width: 880px;
  --font-size-small: 16px;
  font-size: 20px;
}

.list-row-stale {
  font-size: 0.75rem;
}

.list-row-icon {
  width: 1.25rem;
  height: 1.25rem;
}

/* =====================================================================
 * 관리 (차계부) + 비용 기록 시트 -- 2026-10-08
 * docs/DESIGN.md §11, docs/PRD.md §6.1. Everything below is new; it reuses
 * the tokens and the flat-row vocabulary above (.list-row, .session-row,
 * .charge-row-*, .detail-tag, .unit-input, .period-*) and restyles nothing.
 * Dark mode comes from the tokens alone. Car layout scales through rem /
 * --font-size-body / --tap-target.
 * ===================================================================== */

.manage-screen {
  /* Korean wraps at word boundaries, never mid-word. */
  word-break: keep-all;
  overflow-wrap: anywhere;
}

.manage-screen .page-header {
  align-items: center;
}

.manage-add {
  margin-right: calc(var(--space-2) * -1);
  min-height: var(--tap-target);
  padding: 0 var(--space-2);
}

/* 「내보내기」 + 「+ 기록」: two text actions, right-aligned on the title line. */
.manage-actions {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.manage-actions .btn-text {
  min-height: var(--tap-target);
  padding: 0 var(--space-2);
}

/* 내보내기 시트 (components/ExportSheet.tsx) -- reuses .sheet / .sheet-seg. */
.export-nav {
  justify-content: center;
  margin-top: var(--space-2);
}

.export-range {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.export-range-text,
.export-hint {
  margin: var(--space-2) 0 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-variant-numeric: tabular-nums;
  font-weight: 400;
}

.export-check {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-target);
  font-size: 0.9375rem;
}

.export-disclaimer {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

/* 연 보기의 업무 경비 인정액(추정) -- one row + small grey notes. */
.manage-deduction {
  margin: 0 0 var(--space-6);
}

.manage-deduction-value {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.manage-deduction-note {
  margin: var(--space-1) 0 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.manage-deduction-flag {
  margin: var(--space-1) 0 0;
  color: var(--color-danger, #c0392b);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.backup-note {
  margin: 0;
  padding: var(--space-2) 0 var(--space-3);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.period-control {
  max-width: 40rem;
}

/* Previous period stays visible, dimmed, while the next one loads. */
.manage-data {
  transition: opacity 0.15s ease;
}

.manage-data.is-busy {
  opacity: 0.45;
}

.manage-skeleton {
  margin-bottom: var(--space-6);
}

/* The one emphasis of the screen: what the car cost in this period.
   Plain text colour (not green) -- green is for a state, not a total. */
.manage-hero {
  margin: var(--space-4) 0 var(--space-6);
}

.manage-hero-value {
  margin-top: 2px;
  color: var(--color-text);
  font-size: calc(var(--font-size-body) * 2);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.manage-hero-sub {
  margin-top: var(--space-1);
  color: var(--color-text);
  font-size: calc(var(--font-size-body) * 0.9375);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Section title + (optional) text action on the same line. The action
   hangs on negative margins so showing/hiding it never moves the rows. */
.manage-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-1);
}

.manage-section-head .card-title {
  margin: 0;
}

.manage-section-head .btn-text {
  margin-block: -12px;
}

.manage-cats > .manage-section-head + .list-row,
.manage-list > .manage-section-head + .session-row {
  border-top: 1px solid var(--color-border);
}

/* Category rows: icon + name left, won + faint share right. The selected
   row is a quiet grey band (a filter state, not an emphasis). */
.manage-cat.is-selected {
  background: var(--color-bg-sunken);
  box-shadow: calc(var(--space-3) * -1) 0 0 var(--color-bg-sunken), var(--space-3) 0 0 var(--color-bg-sunken);
}

.manage-cat.is-selected .list-row-label {
  font-weight: 700;
}

.manage-cat-value {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  font-size: 0.9375rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.manage-cat-share {
  min-width: 3.4em;
  color: var(--color-text-faint);
  font-size: 0.75rem;
  font-weight: 500;
  text-align: right;
}

/* Ledger rows share the 충전 row look (.session-row / .charge-row-*); these
   only add what the mixed list needs. */
.manage-row .session-sub {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.manage-row-static {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.manage-row-arrow,
.manage-row-arrow-space {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 2px;
}

.manage-row-cat {
  color: var(--color-text-faint);
  font-size: 0.75rem;
  font-weight: 500;
}

/* 예정: faint and dashed -- not a fact yet. */
.manage-row.is-planned {
  border-top: 1px dashed var(--color-text-faint);
}

.manage-row.is-planned + .manage-row {
  border-top: 1px dashed var(--color-text-faint);
}

.manage-row.is-planned .charge-row-toggle {
  opacity: 0.72;
}

.manage-planned-edit {
  display: grid;
  gap: var(--space-2);
}

.manage-planned-edit .unit-input {
  max-width: 14em;
}

.manage-planned-actions {
  align-items: center;
  gap: var(--space-3);
}

.manage-empty {
  padding: var(--space-4) 0;
}

/* ---------- bottom sheet ---------- */

.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
}

.sheet {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: var(--max-content-width);
  max-height: 92vh;
  max-height: 92dvh;
  padding-bottom: env(safe-area-inset-bottom);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--color-bg-elevated);
  color: var(--color-text);
  outline: none;
  animation: sheet-rise 0.2s ease-out;
}

@keyframes sheet-rise {
  from {
    transform: translateY(32px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sheet {
    animation: none;
  }
}

/* Desktop and the car screen: a centred panel, all four corners rounded. */
@media (min-width: 1024px) {
  .sheet-backdrop {
    align-items: center;
  }

  .sheet {
    max-height: 88vh;
    border-radius: var(--radius-lg);
    padding-bottom: 0;
  }
}

@media (pointer: coarse) and (orientation: landscape) and (min-width: 900px) and (min-height: 600px) {
  .sheet-backdrop {
    align-items: center;
  }

  .sheet {
    max-height: 90vh;
    border-radius: var(--radius-lg);
    padding-bottom: 0;
  }
}

html[data-layout="car"] .sheet-backdrop {
  align-items: center;
}

html[data-layout="car"] .sheet {
  max-height: 90vh;
  border-radius: var(--radius-lg);
  padding-bottom: 0;
}

.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4) 0 var(--space-4);
}

.sheet-title {
  font-size: 1.125rem;
  font-weight: 700;
}

.sheet-body {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-2) var(--space-4) var(--space-4);
  overflow-y: auto;
  overscroll-behavior: contain;
  word-break: keep-all;
}

.sheet-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4) var(--space-4);
  border-top: 1px solid var(--color-border);
}

.sheet-save {
  flex: 1;
  min-width: 8em;
}

.sheet-label {
  display: block;
  margin-bottom: var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.sheet-field .input-field {
  width: 100%;
}

.sheet-note {
  margin: 0 0 var(--space-1);
  color: var(--color-text);
  font-size: 0.9375rem;
  font-weight: 500;
}

/* 분류: 5 columns x 2 rows, every tile >= 56px tall. */
.sheet-cats {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-1);
}

.sheet-cat {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 4rem;
  padding: 6px 2px;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
  color: var(--color-text);
  font-family: inherit;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  touch-action: manipulation;
}

.sheet-cat-icon {
  width: 1.5rem;
  height: 1.5rem;
  color: var(--color-text-muted);
}

.sheet-cat.is-on {
  background: var(--color-primary-soft);
  color: var(--color-primary);
  box-shadow: inset 0 0 0 2px var(--color-primary);
}

.sheet-cat.is-on .sheet-cat-icon {
  color: var(--color-primary);
}

.sheet-cat:focus-visible,
.sheet-seg-btn:focus-visible,
.sheet-more:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.sheet-amount {
  min-height: 3.5rem;
  padding-right: 2.5em;
  font-size: 1.5rem;
  font-weight: 700;
  text-align: right;
}

.sheet-field .unit-input .unit-input-suffix {
  font-size: 1rem;
}

.sheet-more {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--tap-target);
  padding: 0;
  border-top: 1px solid var(--color-border);
}

.sheet-more-label {
  color: var(--color-text-muted);
  font-size: 0.9375rem;
  font-weight: 700;
}

.sheet-more-panel {
  display: grid;
  gap: var(--space-3);
}

.sheet-seg {
  display: flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
}

.sheet-seg-btn {
  flex: 1;
  min-height: var(--tap-target);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-muted);
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
}

.sheet-seg-btn.is-on {
  background: var(--color-bg-elevated);
  color: var(--color-text);
  box-shadow: var(--shadow-card);
}

/* 설정 「짧은 정차 합치기」: a small 4-way control on a settings row. */
.sheet-seg.is-compact {
  flex: 0 1 220px;
  min-width: 0;
}

.sheet-seg.is-compact .sheet-seg-btn {
  min-height: 32px;
  font-size: 0.875rem;
}

.merge-setting {
  flex-wrap: wrap;
}

.merge-setting-caption {
  flex-basis: 100%;
  margin: 0;
}

.sheet-repeat-end {
  margin-top: var(--space-3);
}

.sheet-receipts,
.sheet-files {
  display: grid;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
  padding: 0;
  list-style: none;
}

.sheet-receipts li,
.sheet-files li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.sheet-receipt-thumb {
  display: block;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: var(--radius-sm);
  background: var(--color-bg-sunken);
  object-fit: cover;
}

.sheet-file-name {
  min-width: 0;
  overflow: hidden;
  color: var(--color-text);
  font-size: 0.875rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sheet-file-pick {
  position: relative;
  align-self: flex-start;
  gap: var(--space-2);
}

.sheet-file-pick:focus-within {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.sheet-file-icon {
  width: 1.25rem;
  height: 1.25rem;
}

.sheet-file-input {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: 0;
  cursor: pointer;
}

.btn-text.is-danger {
  color: var(--color-danger);
}

/* Planned-row amount reads like every other amount: right-aligned. */
.manage-planned-edit .input-field {
  text-align: right;
}

/* 관리: 휘발유차와 비교 -- secondary to the 유지비 hero (GasolineSavings). */
.manage-gasoline {
  margin: calc(var(--space-6) * -0.25) 0 var(--space-6);
}

.manage-gasoline-title {
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.9375);
  font-weight: 600;
}

.gas-savings {
  margin-top: var(--space-2);
}

.gas-savings-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--font-size-body);
}

.gas-savings-label {
  color: var(--color-text-muted);
}

.gas-savings-value {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.gas-savings-result {
  margin-top: var(--space-1);
  color: var(--color-text);
  font-size: calc(var(--font-size-body) * 1.125);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.gas-savings-result.is-saved {
  color: var(--color-primary);
}

.gas-savings-bars {
  margin-top: var(--space-3);
}

.gas-savings-bars .compare-track {
  height: 6px;
}

.gas-savings-bars .compare-value {
  font-weight: 600;
}

.gas-savings-note,
.gas-savings-basis {
  margin-top: var(--space-2);
  color: var(--color-text-faint);
  font-size: calc(var(--font-size-body) * 0.8125);
}

/* ---------- 운행 타임라인 (2026-10-08, 사장님이 고른 시안) ----------
 * A day = sticky header (date + day km + 지도) and a chain of place nodes
 * joined by trip segments on a thin rail. Only 업무/출퇴근 carry a colour
 * word; undecided trips get one dashed guess chip. */
.map-mount {
  width: 100%;
  height: 100%;
}

/* OSM raster tiles are light-only; invert them in dark mode so a map does
   not flash white at night (Naver uses its own tiles and ignores this). */
@media (prefers-color-scheme: dark) {
  .osm-tiles {
    filter: invert(1) hue-rotate(180deg) brightness(0.92) contrast(0.88);
  }
}

.trip-summary-line {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: var(--tap-target);
  margin-top: var(--space-1);
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text);
  font-family: inherit;
  font-size: calc(var(--font-size-body) * 1.0625);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
}

.trip-summary-line:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.trip-summary-line .summary-details-chevron {
  color: var(--color-text-faint);
}

.trip-summary .trip-summary-body {
  min-height: var(--tap-target);
}

.trip-summary .summary-details {
  margin-top: var(--space-2);
}

.skeleton-line.is-summary-line {
  width: 60%;
  height: 20px;
  margin-top: 14px;
}

.trip-summary {
  margin-bottom: var(--space-6);
}

.timeline {
  margin-top: var(--space-1);
}

.tl-day + .tl-day {
  margin-top: var(--space-5);
}

.tl-day-header {
  position: sticky;
  top: env(safe-area-inset-top);
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 44px;
  margin: 0 calc(var(--space-5) * -1);
  padding: 0 var(--space-5);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-bg);
}

.tl-day-header.is-km-only {
  position: static;
  justify-content: flex-end;
  min-height: 32px;
  border-bottom: 0;
}

.tl-day-title {
  font-size: 0.9375rem;
  font-weight: 800;
  letter-spacing: -0.01em;
}

.tl-day-end {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.tl-day-km {
  font-size: 0.9375rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.tl-day-map-toggle {
  font-size: 0.875rem;
}

.tl-day-map {
  height: 180px;
  margin: var(--space-2) 0;
}

.tl-day-map.is-large {
  height: 220px;
  margin: 0 0 var(--space-4);
}

.tl-day-map.is-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
  font-size: var(--font-size-small);
}

.tl-chain {
  margin: 0;
  padding: var(--space-2) 0 0;
  list-style: none;
}

.tl-chain > li {
  position: relative;
  padding-left: 26px;
}

/* The rail: a 2px line through every segment and every inner node. */
.tl-seg-item::before,
.tl-node::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 6px;
  width: 2px;
  background: var(--color-border);
}

.tl-node.is-first::before {
  top: 50%;
}

.tl-node.is-last::before {
  bottom: 50%;
}

.tl-node.is-first.is-last::before {
  display: none;
}

.tl-node {
  display: flex;
  align-items: center;
  min-height: 30px;
}

.tl-dot {
  position: absolute;
  top: 50%;
  left: 1px;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border: 2px solid var(--color-text-muted);
  border-radius: 50%;
  background: var(--color-bg);
}

.tl-node-label {
  overflow: hidden;
  font-size: 0.9375rem;
  font-weight: 600;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.tl-break {
  height: var(--space-3);
}

.tl-seg-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
}

.tl-seg-toggle {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  padding: 6px 0;
}

.tl-seg-main {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--space-2);
}

.tl-seg-time {
  color: var(--color-text-muted);
  font-size: 0.875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.tl-seg-km {
  font-size: 0.9375rem;
}

.tl-seg-cat {
  color: var(--color-primary);
  font-size: 0.8125rem;
  font-weight: 800;
}

.tl-seg-rule {
  color: var(--color-text-muted);
  font-size: 0.75rem;
}

.tl-seg-memo {
  display: block;
  overflow: hidden;
  margin-top: 1px;
  font-size: 0.8125rem;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.tl-guess-chip {
  position: relative;
  flex: none;
  margin-top: 8px;
  min-height: 28px;
  padding: 0 10px;
  border: 1px dashed var(--color-primary);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-primary);
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
}

/* 28px visual, 44px hit area. */
.tl-guess-chip::after {
  content: "";
  position: absolute;
  top: -8px;
  bottom: -8px;
  left: -4px;
  right: -4px;
}

.tl-guess-chip:disabled {
  opacity: 0.5;
  cursor: default;
}

.tl-guess-chip:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* 운행(journey) row, 2026-10-09: line 1 time · km · duration, faint line 2
   energy (+ rule marker), right side the category word / 업무? / 혼합 and a
   chevron. The category word now sits outside the toggle, aligned with
   line 1. */
.tl-seg-dur {
  color: var(--color-text-muted);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

.tl-seg-sub {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-2);
  margin-top: 1px;
  color: var(--color-text-muted);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.tl-seg-row > .tl-seg-cat {
  flex: none;
  margin-top: 12px;
}

.tl-mixed-chip {
  position: relative;
  flex: none;
  margin-top: 8px;
  min-height: 28px;
  padding: 0 10px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text-muted);
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
}

.tl-mixed-chip::after {
  content: "";
  position: absolute;
  top: -8px;
  bottom: -8px;
  left: -4px;
  right: -4px;
}

.tl-mixed-chip:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.tl-seg-chevron {
  flex: none;
  width: 8px;
  height: 8px;
  margin: 16px 2px 0 0;
  border-right: 2px solid var(--color-text-muted);
  border-bottom: 2px solid var(--color-text-muted);
  transform: rotate(45deg);
  cursor: pointer;
  opacity: 0.7;
}

.tl-seg-chevron.is-open {
  margin-top: 20px;
  transform: rotate(-135deg);
}

.tl-stops {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tl-stops-at {
  font-variant-numeric: tabular-nums;
}

.tl-mixed-hint {
  margin-bottom: var(--space-1);
}

/* 혼합 운행: 저장 waits for a category -- make that visible. */
.tl-seg-panel .btn:disabled {
  opacity: 0.45;
  cursor: default;
}

.tl-legs {
  margin-top: var(--space-2);
}

.tl-legs summary {
  cursor: pointer;
}

.tl-legs ul {
  margin: var(--space-1) 0 0;
  padding: 0;
  list-style: none;
  font-variant-numeric: tabular-nums;
}

.tl-seg-flag {
  margin: 0 0 4px;
}

.tl-seg-panel {
  margin: 0 0 var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
}

.tl-seg-panel .seg,
.tl-seg-panel .input-field {
  background: var(--color-bg);
}

.tl-chain .outing-header {
  border-top: 0;
  padding-top: var(--space-1);
}

@media (min-width: 1024px) {
  .tl-day-header {
    margin: 0;
    padding: 0;
  }
}

/* ---------- 읽기 전용 공유 (설정 → 공유, 공개 페이지 /s/<token>) ----------
 * 2026-10-09. 평평한 목록형(SCREENS §0-2) 규칙 그대로: 상자 없이 제목 + 얇은 줄.
 * 공개 페이지는 서버 렌더 HTML이라 탭바가 없다 -- 아래 여백만 일반 페이지로 되돌린다. */
.share-content {
  padding-bottom: calc(var(--space-7) + env(safe-area-inset-bottom));
}

.share-disclaimer {
  margin: 0 0 var(--space-2);
  color: var(--color-warning-text);
  font-size: var(--font-size-small);
  font-weight: 600;
}

.share-downloads {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: var(--space-5) 0 0;
}

.share-row {
  padding: var(--space-3) 0;
}

.share-row + .share-row {
  border-top: 1px solid var(--color-border);
}

.share-row-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.share-when {
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: 0.9375rem;
  font-weight: 600;
}

.share-amount {
  flex: none;
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.share-row-sub {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  margin-top: var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  overflow-wrap: anywhere;
}

.share-chip {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 8px;
  border-radius: var(--radius-pill);
  background: var(--color-bg-sunken);
  color: var(--color-text-muted);
  font-size: 12px;
  font-weight: 600;
}

.share-chip.is-work {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

.share-flag {
  color: var(--color-warning-text);
  font-weight: 600;
}

.share-empty,
.share-more,
.share-note {
  margin: var(--space-3) 0 0;
  color: var(--color-text-faint);
  font-size: var(--font-size-small);
}

.share-note {
  margin-top: var(--space-7);
}

/* 설정 → 공유 */
.share-form {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-3) 0 0;
}

.share-form fieldset {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.share-form legend {
  padding: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.address-detail {
  min-width: 0;
  margin: var(--space-3) 0 0;
  padding: 0;
  border: 0;
}

.address-detail legend {
  padding: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.share-choices {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  margin-top: var(--space-1);
}

.share-choices label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  font-size: 0.9375rem;
  font-weight: 500;
}

.share-dates {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}

.share-dates label,
.share-form > label {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.share-link-row {
  padding: var(--space-3) 0;
}

.share-link-row + .share-link-row {
  border-top: 1px solid var(--color-border);
}

.share-link-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.share-link-meta {
  margin-top: var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  overflow-wrap: anywhere;
}

.share-link-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.share-created {
  margin: var(--space-3) 0 0;
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-primary-soft);
  font-size: var(--font-size-small);
}

.share-created code {
  display: block;
  margin: var(--space-2) 0;
  font-size: 0.8125rem;
  overflow-wrap: anywhere;
  user-select: all;
}

.share-access-list {
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
  color: var(--color-text-muted);
  font-size: 12px;
}

.share-access-list li {
  padding: 2px 0;
  overflow-wrap: anywhere;
}

.share-form > label.settings-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  color: var(--color-text);
  font-size: 0.9375rem;
}

/* 가져오기 시트 (components/ImportSheet.tsx) + 충전 영수증 (components/ChargeReceipts.tsx)
   -- flat rows, text actions, no cards; reuses .sheet / .sheet-seg / .sheet-more. */
.import-file-name {
  margin: var(--space-1) 0 0;
  overflow: hidden;
  color: var(--color-text);
  font-size: 0.875rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.import-map {
  display: grid;
  gap: var(--space-2);
  padding-top: var(--space-2);
}

.import-map-row {
  display: grid;
  grid-template-columns: minmax(0, 8.5rem) minmax(0, 1fr);
  align-items: center;
  gap: var(--space-2);
}

.import-map-label {
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.import-map-row .input-field {
  min-width: 0;
}

.import-summary {
  margin: 0 0 var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.import-rows,
.import-batches {
  margin: 0;
  padding: 0;
  list-style: none;
}

.import-row,
.import-batches li {
  padding: var(--space-2) 0;
  border-top: 1px solid var(--color-border);
}

.import-row:not(.is-applied) .import-row-main {
  opacity: 0.6;
}

.import-row-main {
  display: grid;
  grid-template-columns: 3.9rem minmax(0, 1fr) auto;
  align-items: baseline;
  gap: var(--space-2);
}

.import-row-when {
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

.import-row-label {
  min-width: 0;
  font-size: 0.9375rem;
  overflow-wrap: anywhere;
}

.import-row-money {
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  text-align: right;
  white-space: nowrap;
}

.import-row-sub {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--space-3);
  margin-top: 2px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.import-row-sub .btn-text,
.import-confirm .btn-text,
.import-batches .btn-text {
  min-height: 36px;
  font-size: var(--font-size-small);
}

.import-row-state {
  margin-right: auto;
}

.import-cat {
  max-width: 100%;
  min-height: 36px;
  padding: 0 var(--space-1);
  border: 0;
  background: transparent;
  color: var(--color-primary);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.import-batches li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.import-batch-main {
  display: grid;
  min-width: 0;
}

.import-batch-name {
  overflow: hidden;
  font-size: 0.9375rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.import-batch-sub {
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.import-confirm,
.import-actions {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: var(--space-2);
}

.charge-receipts {
  margin-top: var(--space-3);
}

.charge-receipts .sheet-receipts {
  margin-bottom: var(--space-1);
}

/* ---------- 차량 전환 · 정비 알림 줄 · 백업 복원 (2026-10-09) ----------
 * Same flat language as the rest: text actions, hairlines, no cards or grey
 * buttons. Colours/spacing are tokens only, so dark mode follows. */

/* Next to a screen title: 「모델 Y ▾」. margin-right:auto keeps the title's
   other actions (새로고침, 내보내기) on the far right. */
.page-header .vehicle-switch {
  position: relative;
  align-self: center;
  margin-right: auto;
  margin-left: var(--space-1);
}

.vehicle-switch-button {
  color: var(--color-text-muted);
  font-size: 0.9375rem;
  font-weight: 600;
}

.vehicle-switch-caret {
  width: 1rem;
  height: 1rem;
}

.vehicle-switch-list {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  min-width: 12rem;
  padding: var(--space-1) 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg-elevated);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.12);
}

.vehicle-switch-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 44px;
  padding: 0 var(--space-4);
  border: 0;
  background: transparent;
  color: var(--color-text);
  font-family: inherit;
  font-size: 0.9375rem;
  text-align: left;
  cursor: pointer;
}

.vehicle-switch-item.is-selected {
  color: var(--color-primary);
  font-weight: 700;
}

.vehicle-switch-vin {
  color: var(--color-text-faint);
  font-size: var(--font-size-small);
  font-weight: 400;
}

/* 정비 알림: ONE quiet line under the title; the list opens only on tap. */
.reminder-line {
  margin: calc(var(--space-2) * -1) 0 var(--space-3);
}

.reminder-line-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  font-family: inherit;
  font-size: 0.875rem;
  text-align: left;
  cursor: pointer;
}

.reminder-line.is-overdue .reminder-line-toggle {
  color: var(--color-warning-text);
}

.reminder-line-dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.reminder-line-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.reminder-line-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.reminder-line-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-top: 1px solid var(--color-border);
  font-size: 0.875rem;
}

.reminder-line-item {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.reminder-line-sub {
  color: var(--color-text-faint);
  font-size: var(--font-size-small);
}

.reminder-overdue-text {
  color: var(--color-warning-text);
  font-weight: 700;
}

/* 충전 손실 detail inside an opened charge row */
.charge-loss-detail {
  margin-top: var(--space-2);
}

/* 백업에서 복원 */
.restore-pick {
  position: relative;
  overflow: hidden;
}

.restore-pick:focus-within {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.restore-pick.is-disabled {
  color: var(--color-text-faint);
}

.restore-file-input {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: 0;
  cursor: pointer;
}

.restore-file-input:disabled {
  cursor: default;
}

.restore-panel {
  padding: var(--space-2) 0 var(--space-3);
}

.restore-title {
  margin: 0 0 var(--space-1);
  font-size: 0.9375rem;
  font-weight: 700;
}

.restore-line {
  margin: 0 0 var(--space-1);
  font-size: 0.875rem;
  overflow-wrap: anywhere;
}

.restore-warning {
  color: var(--color-warning-text);
}

.restore-faint {
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

/* A title row with a vehicle switch is crowded on a phone: the title and the
   actions never wrap; the switch gives way (ellipsis) instead. */
.page-header:has(.vehicle-switch) h1,
.page-header:has(.vehicle-switch) .manage-actions,
.page-header:has(.vehicle-switch) .manage-actions .btn-text,
.page-header:has(.vehicle-switch) .status-refresh {
  flex: none;
  white-space: nowrap;
}

.page-header .vehicle-switch {
  min-width: 0;
}

.vehicle-switch-button {
  max-width: 100%;
}

.vehicle-switch-button .vehicle-switch-caret {
  flex: none;
}

.page-header:has(.vehicle-switch) .manage-actions {
  gap: 0;
}

/* 배터리 가늠 (관리 히어로 아래 희미한 줄) -- 아래 블록의 음수 마진 때문에 자기 간격을 가진다 */
.battery-gauge {
  margin: var(--space-2) 0 var(--space-6);
}

.battery-gauge .battery-gauge-basis {
  margin-top: 2px;
}

/* ---------- 설정 2단 구조 (2026-10-12): 홈 분류 목록 + 하위 화면 머리글 ----------
 * Appended block only -- existing rules above are untouched. */
.settings-home {
  display: flex;
  flex-direction: column;
}

.settings-home-row {
  min-height: 64px;
  color: inherit;
  text-decoration: none;
}

.settings-sub-header {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
}

.settings-back {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  min-height: 36px;
  color: var(--color-text-muted);
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
}

.settings-back .list-row-chevron {
  transform: rotate(90deg);
}

.settings-home-row .list-row-chevron {
  transform: rotate(-90deg);
}

/* ---------- 차를 바꿔서 아낀 돈 (2026-10-12) ----------
 * SwitchSavings: faint per-fuel breakdown lines sit tight under the bars,
 * the payback line reads as a muted aside. */
.gas-savings > .gas-savings-basis {
  margin-top: var(--space-1);
}

.gas-savings > .gas-savings-bars + .gas-savings-basis {
  margin-top: var(--space-2);
}

/* 충전비 = 실제 결제 + 추정 (2026-11-12): one muted line under the bars. */
.gas-savings-split {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.8125);
  font-variant-numeric: tabular-nums;
  word-break: keep-all;
  overflow-wrap: anywhere;
}

.gas-savings > .gas-savings-split + .gas-savings-basis {
  margin-top: var(--space-1);
}

.gas-savings-payback {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.875);
  font-variant-numeric: tabular-nums;
}

/* 차를 바꿔서 아낀 돈: 「계산 근거」 disclosure, collapsed by default. */
.gas-savings-details {
  margin-top: var(--space-2);
}

.gas-savings-details > summary {
  color: var(--color-text-faint);
  font-size: calc(var(--font-size-body) * 0.8125);
  cursor: pointer;
}

/* ======================================================================
 * 상태 홈 (StatusScreen.tsx, 2026-10-12 사장님: 「테슬라 앱 짜임」 + 「내 차 그림」)
 * Phone, one column: header > battery > car > charge-limit gauge >
 *   alerts/to-dos (only when present) > flat menu list.
 * Car layout: two columns -- left = header..gauge, right = alerts + menu.
 * Flat: no cards, hairlines between menu rows, one green accent.
 * ====================================================================== */
.status-home {
  display: flex;
  flex-direction: column;
}

.status-header {
  align-items: center;
  margin-bottom: var(--space-4);
}

.status-car-name {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Several cars: the switch already names the car. */
.status-header:has(.vehicle-switch) .status-car-name {
  display: none;
}

.status-header .vehicle-switch {
  margin-right: auto;
}

/* settings -> 차 이름 */
.car-name-row label {
  display: block;
  margin-bottom: 6px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 600;
}

.car-name-row + .car-name-row {
  margin-top: var(--space-3);
}

.car-name-field {
  display: flex;
  gap: var(--space-2);
}

.car-name-field .input-field {
  flex: 1;
  min-width: 0;
}

.car-name-row .form-error,
.car-name-row .form-message {
  margin-top: 6px;
}

.car-name-row ~ .form-hint {
  margin-top: var(--space-3);
}

/* --- car picture --- */
.car-view {
  position: relative;
  margin: var(--space-2) auto var(--space-4);
  width: 100%;
  max-width: 420px;
}

.car-photo-wrap {
  position: relative;
}

.car-photo {
  display: block;
  width: 100%;
  height: auto;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* state markers on the photo: dot + short label, positions in % of the picture */
.car-marks {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.car-mark {
  position: absolute;
  width: 0;
  height: 0;
}

.car-mark-dot {
  position: absolute;
  left: -6px;
  top: -6px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--color-danger);
  box-shadow: 0 0 0 3px var(--color-bg);
}

.car-mark.is-charge .car-mark-dot {
  background: var(--color-primary);
}

.car-mark-glow {
  position: absolute;
  left: -16px;
  top: -16px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--color-primary);
  opacity: 0.35;
  filter: blur(5px);
}

.car-mark-label {
  position: absolute;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--color-danger);
  color: var(--color-danger-contrast);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
}

.car-mark.is-charge .car-mark-label {
  background: var(--color-primary);
  color: #fff;
}

.car-mark.is-below .car-mark-label {
  left: 0;
  top: 11px;
  transform: translateX(-50%);
}

.car-mark.is-left .car-mark-label {
  right: 12px;
  top: 0;
  transform: translateY(-50%);
}

.car-mark.is-right .car-mark-label {
  left: 12px;
  top: 0;
  transform: translateY(-50%);
}

/* parts the current angle can't show (other side, front/rear end): text chips under the photo */
.car-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-1);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}

.car-chip {
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--color-danger-soft);
  color: var(--color-danger);
  font-size: 12px;
  font-weight: 600;
}

/* lock / sentry badges, top-left of the picture */
.car-badges {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  gap: var(--space-1);
}

.car-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--color-bg-sunken);
  color: var(--color-text-muted);
}

.car-badge.is-alert {
  background: var(--color-danger-soft);
  color: var(--color-danger);
}

.car-badge-icon {
  width: 16px;
  height: 16px;
}

.car-badge-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--color-danger);
  box-shadow: 0 0 0 3px var(--color-danger-soft);
}

/* --- hero --- */
.dash-hero {
  text-align: center;
}

.dash-hero .status-battery {
  justify-content: center;
}

.dash-status-line {
  margin-top: var(--space-1);
  color: var(--color-text-muted);
  font-size: calc(var(--font-size-body) * 0.9375);
  font-weight: 600;
  text-align: center;
  word-break: keep-all;
}

.dash-status-line.is-charging {
  color: var(--color-primary);
}

.dash-line {
  margin-top: 6px;
  color: var(--color-text-faint);
  font-size: var(--font-size-small);
  font-variant-numeric: tabular-nums;
  word-break: keep-all;
}

.dash-line-center {
  text-align: center;
}

.dash-gauge {
  margin-bottom: var(--space-5);
}

/* --- battery bar + charge limit --- */
.charge-limit {
  margin-top: var(--space-4);
}

.dash-battery-bar {
  position: relative;
  display: block;
  height: 10px;
  overflow: visible;
}

.dash-battery-bar .battery-bar-fill {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  display: block;
}

.dash-battery-bar .battery-bar-fill.is-charging {
  background-image: repeating-linear-gradient(-45deg, rgba(255, 255, 255, 0) 0 8px, rgba(255, 255, 255, 0.22) 8px 16px);
}

.dash-limit-zone {
  position: absolute;
  top: 0;
  bottom: 0;
  display: block;
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
  background: var(--color-primary-soft);
}

.dash-limit-caption {
  margin: 0 0 var(--space-1);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

.dash-limit-caption b {
  color: var(--color-text);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* The bar is the slider: a transparent native range input sits exactly on it.
   The input is 28px wider than the bar and the thumb is 28px, so the thumb
   centre travels 0..100% of the bar and lines up with the percent scale. */
.dash-limit-track {
  position: relative;
  display: flex;
  align-items: center;
  height: 28px;
}

.dash-limit-track .dash-battery-bar {
  flex: 1 1 auto;
}

.dash-limit-input {
  position: absolute;
  top: 0;
  left: -14px;
  width: calc(100% + 28px);
  height: 28px;
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  touch-action: pan-y;
  -webkit-appearance: none;
  appearance: none;
}

.dash-limit-input:disabled {
  cursor: default;
}

.dash-limit-input::-webkit-slider-runnable-track {
  height: 28px;
  background: transparent;
}

.dash-limit-input::-moz-range-track {
  height: 28px;
  background: transparent;
}

.dash-limit-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 28px;
  height: 28px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

.dash-limit-input::-moz-range-thumb {
  box-sizing: border-box;
  width: 28px;
  height: 28px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

.dash-limit-input:disabled::-webkit-slider-thumb {
  opacity: 0.7;
}

.dash-limit-input:disabled::-moz-range-thumb {
  opacity: 0.7;
}

.dash-limit-track.is-empty .dash-limit-input::-webkit-slider-thumb {
  visibility: hidden;
}

.dash-limit-track.is-empty .dash-limit-input::-moz-range-thumb {
  visibility: hidden;
}

.dash-limit-input:focus-visible {
  outline: none;
}

.dash-limit-input:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 3px var(--color-primary);
}

.dash-limit-input:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 3px var(--color-primary);
}

/* 50% / 100% ticks, with 일상 (50-90) and 장거리 (90-100) beneath. */
.dash-limit-scale {
  position: relative;
  height: 34px;
  margin-top: 2px;
  color: var(--color-text-faint);
  font-size: 12px;
  line-height: 14px;
  font-variant-numeric: tabular-nums;
}

.dash-limit-tick {
  position: absolute;
  top: 0;
}

.dash-limit-tick.is-mid {
  left: 50%;
  transform: translateX(-50%);
}

.dash-limit-tick.is-end {
  right: 0;
}

.dash-limit-zone-label {
  position: absolute;
  top: 18px;
  box-sizing: border-box;
  padding-top: 2px;
  border-top: 1px solid var(--color-border);
  font-size: 11px;
  text-align: center;
  white-space: nowrap;
}

.dash-limit-zone-label.is-daily {
  left: 50%;
  width: 39%;
}

.dash-limit-zone-label.is-trip {
  left: 90%;
  right: 0;
}

.dash-limit-status {
  margin: var(--space-1) 0 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  word-break: keep-all;
}

.dash-limit-status.form-error {
  color: var(--color-danger);
}

/* --- alerts / to-dos (only when present) --- */
.dash-health {
  margin-bottom: var(--space-3);
}

.dash-health:empty {
  display: none;
}

.dash-health > .alert {
  margin-bottom: var(--space-2);
}

.dash-ok,
.dash-anomaly {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  font-size: calc(var(--font-size-body) * 0.9375);
}

.dash-ok {
  min-height: 28px;
  color: var(--color-text-faint);
  font-size: 12px;
  font-weight: 600;
}

.dash-ok .list-row-icon {
  width: 14px;
  height: 14px;
}

.dash-ok-icon {
  color: var(--color-primary);
}

.dash-ok-detail {
  margin-left: auto;
  color: var(--color-text-faint);
  font-size: 12px;
  font-weight: 500;
}

.dash-anomaly {
  margin-bottom: var(--space-1);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-danger-soft);
  color: var(--color-danger);
  font-weight: 600;
}

.dash-anomaly .list-row-icon {
  color: var(--color-danger);
}

/* Severity (2026-10-29): car alerts are worded and coloured by how much they
   matter -- 주의 노랑 / 위험 빨강 / 정보 회색 (frontend/src/shared/vehicleAlerts.ts). */
.dash-anomaly.is-warn {
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
}

.dash-anomaly.is-warn .list-row-icon {
  color: var(--color-warning-text);
}

.dash-anomaly.is-info {
  background: var(--color-surface-muted, rgba(139, 149, 161, 0.12));
  color: var(--color-text-muted);
  font-weight: 500;
}

.dash-anomaly.is-info .list-row-icon {
  color: var(--color-text-muted);
}

.dash-anomaly-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  word-break: keep-all;
}

.dash-anomaly-code {
  font-size: 11px;
  font-weight: 400;
  opacity: 0.75;
  word-break: break-all;
}

.dash-anomaly-age {
  margin-left: auto;
  flex-shrink: 0;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 500;
  opacity: 0.8;
}

.dash-todo {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text);
  font-size: var(--font-size-small);
  font-weight: 600;
  text-decoration: none;
}

.dash-todo-chevron {
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--color-text-faint);
}

.dash-todo-dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-primary);
}

.dash-todo:last-of-type {
  border-bottom: none;
}

.dash-todo.is-urgent .dash-todo-dot {
  background: var(--color-danger);
}

.dash-todo-text {
  flex: 1;
  min-width: 0;
}

/* --- menu list (테슬라 앱 홈 아래쪽 목록) --- */
.menu-list {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--color-border);
}

.menu-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  min-height: 64px;
  padding: var(--space-2) 0;
  border: none;
  border-bottom: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  touch-action: manipulation;
}

.menu-row.is-static {
  cursor: default;
}

.menu-row:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
  border-radius: var(--radius-sm);
}

.menu-row-icon {
  flex: none;
  width: 24px;
  height: 24px;
  color: var(--color-text);
}

.menu-row-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
}

.menu-row-title {
  font-size: var(--font-size-body);
  font-weight: 600;
  line-height: 1.3;
}

.menu-row-summary {
  overflow: hidden;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  line-height: 1.4;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
}

.menu-row-stale {
  margin-left: var(--space-2);
  color: var(--color-text-faint);
}

.menu-row-chevron,
.menu-row .list-row-chevron {
  flex: none;
  width: 18px;
  height: 18px;
  color: var(--color-text-faint);
}

.menu-row-group.is-open > .menu-row {
  border-bottom-color: transparent;
}

.menu-row-panel {
  padding: 0 0 var(--space-3) calc(24px + var(--space-4));
  border-bottom: 1px solid var(--color-border);
}

.menu-row-panel .list-row:last-child {
  border-bottom: none;
}

.menu-row-panel .tyre-line {
  margin-top: 0;
}

.menu-row-extra {
  padding: 0 0 var(--space-2) calc(24px + var(--space-4));
  border-bottom: 1px solid var(--color-border);
}

.menu-row-extra .status-save-place {
  min-height: 36px;
}

/* --- car layout: car left, alerts + menu right --- */
@media (pointer: coarse) and (orientation: landscape) and (min-width: 900px) and (min-height: 600px) {
  .content:has(.status-home) {
    --max-content-width: 1180px;
  }

  .status-home {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: var(--space-7);
    align-items: start;
  }

  .status-home-car {
    position: sticky;
    top: var(--space-4);
  }

  .status-home .car-view {
    max-width: 560px;
  }
}

html[data-layout="car"] .content:has(.status-home) {
  --max-content-width: 1180px;
}

html[data-layout="car"] .status-home {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: var(--space-7);
  align-items: start;
}

html[data-layout="car"] .status-home-car {
  position: sticky;
  top: var(--space-4);
}

html[data-layout="car"] .status-home .car-view {
  max-width: 560px;
}

/* --- 출발 예약 프리컨디셔닝 card (2026-10-31): flat, tokens only --- */
.precond {
  margin: var(--space-5) 0;
  padding: var(--space-4) 0 0;
  border-top: 1px solid var(--color-border);
}

.precond-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.precond-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
}

.precond-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--color-bg-sunken);
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
  white-space: nowrap;
}

.precond-chip.is-running {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

.precond-chip.is-scheduled {
  background: var(--color-bg-sunken);
  background: color-mix(in srgb, #3b82f6 14%, var(--color-bg));
  color: #3478e6;
}

.precond-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

.precond-line {
  margin: 6px 0 0;
  font-size: var(--font-size-small);
  font-variant-numeric: tabular-nums;
  word-break: keep-all;
}

.precond-line.is-faint {
  color: var(--color-text-faint);
}

.precond-line.is-heat {
  color: var(--color-primary);
  font-weight: 700;
}

.precond-block {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.precond-sub {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 700;
}

.precond-reservation {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.precond-when {
  font-size: 1.0625rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.precond-quick {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.precond-quick .btn,
.precond-picker .btn {
  min-height: 44px;
  padding: 0 4px;
}

.precond-picker {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.precond-days {
  display: flex;
  gap: var(--space-2);
}

.precond-time {
  flex: 1 1 110px;
  min-width: 0;
  min-height: 44px;
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--color-bg-sunken);
  color: var(--color-text);
  font-family: inherit;
  font-size: 16px;
}

.precond-confirm {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
  font-size: var(--font-size-small);
  font-weight: 600;
}

.precond-confirm p {
  margin: 0 0 var(--space-2);
}

.precond-confirm-actions {
  display: flex;
  gap: var(--space-2);
}

/* ---------- 토스트 · 스낵바 (components/Toast.tsx, UX 개선 2026-11-07) ----------
 * 탭바 바로 위 가운데 한 개. 들어올 때 이즈 아웃 200ms(아래에서 8px),
 * 움직임 줄이기 설정이면 움직임 없이 바로. */
.toast-region {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(64px + env(safe-area-inset-bottom) + var(--space-3));
  z-index: 40;
  display: flex;
  justify-content: center;
  padding: 0 var(--space-4);
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: 480px;
  width: 100%;
  min-height: var(--tap-target);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  background: #191f28;
  color: #ffffff;
  font-size: 15px;
  line-height: 1.4;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
  animation: toast-in 200ms cubic-bezier(0.22, 1, 0.36, 1);
}

.toast.is-error .toast-message::before {
  content: '';
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: var(--space-2);
  border-radius: 50%;
  background: #ff6369;
  vertical-align: 1px;
}

.toast-message {
  flex: 1;
  min-width: 0;
}

.toast-action {
  flex: none;
  min-height: 40px;
  min-width: 64px;
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: #5ee0a8;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

.toast-action:focus-visible {
  outline: 2px solid #5ee0a8;
  outline-offset: 2px;
}

@media (prefers-color-scheme: dark) {
  .toast {
    background: #eceff3;
    color: #191f28;
  }
  .toast-action {
    color: #0f8a5f;
  }
  .toast-action:focus-visible {
    outline-color: #0f8a5f;
  }
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* 공용 스켈레톤 블록 (components/Skeleton.tsx) -- 기존 .skeleton-line 색을 그대로 쓴다. */
.skeleton-block {
  display: block;
  border-radius: var(--radius-md);
  background: rgba(127, 127, 127, 0.14);
}

.screen-skeleton {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* 로딩 버튼: 처리 중이면 글자 옆에 작은 링, 다시 누를 수 없다. */
.btn.is-loading {
  cursor: progress;
}

.btn-spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-right: 6px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  vertical-align: -2px;
  animation: btn-spin 700ms linear infinite;
}

@keyframes btn-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .toast {
    animation: none;
  }
  .btn-spinner {
    animation-duration: 1600ms;
  }
}

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- UX 2026-11-07: 상태 ---------- */
.status-skeleton { display: grid; gap: var(--space-3); }

/* ---------- UX 2026-11-07: 관리·충전 ---------- */
.manage-empty { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); }

/* ---------- UX 2026-11-07: 운행 ---------- */
.trip-empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

.trip-empty .btn {
  min-height: var(--tap-target);
}

/* 운행 목록: 다음 기간을 불러오는 동안 옛 줄은 흐리게(요약과 어긋나 보이지 않게). */
.trip-list.is-stale {
  opacity: 0.4;
  transition: opacity 150ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .trip-list.is-stale {
    transition: none;
  }
}

/* ---------- 오류 안내 (components/ErrorNotice.tsx, docs/ERRORS.md) ----------
 * 제목(무슨 문제) -> 해결 한 줄 -> 버튼 -> 작은 회색 꼬리표. 자리별 4가지 모양. */
.error-notice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  min-width: 0;
}

.error-notice-body {
  min-width: 0;
  max-width: 100%;
}

.error-notice-title {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--color-text);
  overflow-wrap: anywhere;
}

.error-notice-fix {
  margin: var(--space-1) 0 0;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--color-text-muted);
  overflow-wrap: anywhere;
}

.error-notice-code {
  display: block;
  margin-top: var(--space-1);
  font-size: 0.75rem;
  color: var(--color-text-faint);
}

.error-notice-action {
  align-self: flex-start;
  min-height: 44px;
}

/* 화면 전체 실패: 가운데 정렬 오류 상태 화면 */
.error-notice.is-screen {
  align-items: center;
  text-align: center;
  padding: var(--space-7) var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--color-bg-sunken);
}

.error-notice.is-screen .error-notice-title {
  font-size: 1.0625rem;
}

.error-notice.is-screen .error-notice-action {
  align-self: center;
}

/* 한 구역 실패: 구역 안의 조용한 블록 */
.error-notice.is-section {
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
}

/* 계속되는 상태: 왼쪽 점 + 줄 배너. 폭이 되면 버튼이 오른쪽에 붙는다 */
.error-notice.is-banner {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-warning-bg);
  margin-bottom: var(--space-3);
}

.error-notice.is-banner .error-notice-title {
  color: var(--color-warning-text);
}

.error-notice.is-banner.is-self-resolving {
  background: var(--color-bg-sunken);
}

.error-notice.is-banner.is-self-resolving .error-notice-title {
  color: var(--color-text);
}

/* 저장·명령 실패: 칸 바로 아래 */
.error-notice.is-inline {
  gap: var(--space-2);
  padding: var(--space-2) 0;
}

.error-notice.is-inline .error-notice-title {
  color: var(--color-danger);
  font-size: 0.8125rem;
}

.error-notice.is-inline .error-notice-fix {
  font-size: 0.8125rem;
}

@media (prefers-color-scheme: dark) {
  .error-notice.is-banner {
    background: rgba(255, 200, 80, 0.12);
  }
  .error-notice.is-banner .error-notice-title {
    color: #f5c86b;
  }
}

.error-notice.is-screen .btn-secondary,
.error-notice.is-section .btn-secondary {
  background: var(--color-bg-elevated);
}

.error-notice-title,
.error-notice-fix {
  word-break: keep-all;
}

/* ======================================================================
 * UI 2026-11-08 -- grouped surfaces, five-step type, icon chips.
 * (사장님: the screens themselves had to look better, not just their states.)
 *
 * Kept from before: flat rows, one green accent, no borders, tokens-only dark
 * mode, every class name (Jinja pages share them). Changed everywhere at
 * once through shared classes rather than per screen:
 *   - type: display 56 (battery) / hero 34 (a screen's one total) /
 *     title 26 (h1) / section 18 / body 16 / caption 13, tabular numerals.
 *   - page is light grey; sections (.card, menu list, controls) are white
 *     radius-16 surfaces, 16px inner gutter, 12px between groups, 32px
 *     before a new titled group.
 *   - list rows 56px; row icons sit in a 36px tinted chip (same 24px
 *     viewBox / 1.8 stroke as the tab bar).
 *   - bottom tabs: active = green icon in a soft pill.
 * ====================================================================== */

:root {
  --bottom-nav-height: 66px;
}

body {
  font-family: "Pretendard", -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Malgun Gothic",
    "맑은 고딕", "Noto Sans KR", "Segoe UI", sans-serif;
  word-break: keep-all;
  overflow-wrap: break-word;
}

::selection {
  background: var(--color-primary-soft);
  color: var(--color-text);
}

input,
textarea {
  caret-color: var(--color-primary);
}

/* ---- titles ---- */
.page-header {
  margin-bottom: var(--space-5);
}

h1,
.page-header h1 {
  font-size: var(--font-size-title);
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -0.03em;
}

.card-title,
.group-title,
.manage-gasoline-title,
.precond-title {
  color: var(--color-text);
  font-size: var(--font-size-section);
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: -0.02em;
}

/* ---- grouped surface ---- */
.card,
.menu-list,
.dash-health,
.dash-gauge,
.precond,
.manage-gasoline,
.settings-home,
.charge-hero,
.manage-hero,
.tl-day {
  margin: 0 0 var(--space-3);
  padding: var(--space-1) var(--space-4);
  border: 0;
  border-radius: var(--radius-group);
  background: var(--color-surface);
}

.card .card {
  margin: 0;
  padding: 0;
  border-radius: 0;
  background: transparent;
}

/* Non-row content first/last in a surface gets the surface's own breathing
   room (rows bring their own 56px). A divider never opens a surface. */
.card > :first-child:not(.list-row):not(.session-row):not(.stat-row):not(.card-title):not(.manage-section-head):not(h2):not(.list-row-group):not(details):not(.place-row) {
  margin-top: var(--space-4);
}

.card > :last-child:not(.list-row):not(.session-row):not(.stat-row):not(.list-row-group):not(details):not(.place-row) {
  margin-bottom: var(--space-4);
}

.card > :first-child {
  border-top: 0;
}

/* A titled group: title on the surface's first line, rows under it. */
.card > .card-title:first-child,
.card > .manage-section-head:first-child,
.card > h2:first-child {
  padding: var(--space-4) 0 var(--space-2);
}

.card > .card-title:first-child + .list-row,
.card > .card-title:first-child + .session-row,
.card > .card-title:first-child + .stat-row,
.manage-cats > .manage-section-head + .list-row,
.manage-list > .manage-section-head + .session-row,
.charge-list .card-title + .session-row,
.trip-list .card-title + .session-row {
  border-top: 0;
  padding-top: var(--space-2);
}

/* A title that sits ABOVE a surface (settings "운행 표시", "백업" ...). */
.group-title {
  margin: var(--space-7) var(--space-1) var(--space-2);
}

.group-title:first-child {
  margin-top: 0;
}

/* stat rows on a surface: the surface pads itself */
.card > .stat-row:first-child {
  padding-top: var(--space-4);
}

.card > .stat-row:last-child {
  padding-bottom: var(--space-4);
}

.stat-label {
  color: var(--color-text);
  font-size: var(--font-size-body);
  font-weight: 500;
}

.stat-value {
  font-size: var(--font-size-body);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ---- rows ---- */
.list-row {
  min-height: 56px;
}

.list-row-label {
  font-size: var(--font-size-body);
  font-weight: 500;
}

.list-row-sub {
  margin-top: 1px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 400;
  line-height: 1.4;
}

.list-row-value {
  font-size: var(--font-size-body);
}

/* icon chips: 36px tinted square, 20px glyph (24px viewBox, 1.8 stroke) */
.list-row-label > .list-row-icon,
.menu-row-icon,
.dash-anomaly > .list-row-icon {
  flex: none;
  box-sizing: border-box;
  width: var(--icon-chip);
  height: var(--icon-chip);
  padding: 8px;
  border-radius: 10px;
  background: var(--icon-chip-bg);
  color: var(--color-text);
}

html[data-layout="car"] .list-row-label > .list-row-icon,
html[data-layout="car"] .menu-row-icon {
  width: 2.25rem;
  height: 2.25rem;
  padding: 0.5rem;
}

.list-row-chevron,
.menu-row-chevron,
.menu-row .list-row-chevron,
.dash-todo-chevron {
  width: 20px;
  height: 20px;
  color: var(--color-text-faint);
}

/* ---- buttons ---- */
/* Tonal button: a real, obviously tappable control on a white surface
   (the precondition time picks used to read as thin text). */
.btn-tonal {
  min-height: 44px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-bg-sunken);
  color: var(--color-text);
  font-size: 0.9375rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.btn-tonal.is-on,
.btn-tonal[aria-pressed="true"] {
  background: var(--color-primary-soft);
  color: var(--color-primary);
  box-shadow: inset 0 0 0 1.5px var(--color-primary);
}

.btn-tonal:disabled,
.btn-primary:disabled {
  opacity: 0.5;
  cursor: default;
}

@media (hover: hover) {
  .btn-tonal:not(:disabled):hover {
    filter: brightness(0.97);
  }

  .list-row-button:not(:disabled):hover,
  .menu-row:not(.is-static):hover,
  .dash-todo:hover {
    background: color-mix(in srgb, var(--color-text) 3%, transparent);
  }
}

/* ---- bottom tab bar ---- */
.bottom-tab-bar {
  padding: 6px var(--space-2) env(safe-area-inset-bottom);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
}

.bottom-tab-bar::after {
  background: var(--color-surface);
}

.bottom-tab-bar .tab-link {
  gap: 2px;
  min-height: 52px;
  padding: 2px 0 4px;
  color: var(--color-text-muted);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.bottom-tab-bar .tab-link-icon {
  box-sizing: content-box;
  width: 22px;
  height: 22px;
  padding: 4px 18px;
  border-radius: var(--radius-pill);
  transition: background-color 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.bottom-tab-bar .tab-link[aria-current="page"] {
  color: var(--color-primary);
  font-weight: 700;
}

.bottom-tab-bar .tab-link[aria-current="page"] .tab-link-icon {
  background: var(--color-primary-soft);
  stroke-width: 2.1;
}

/* ---- 상태 ---- */
.status-header {
  margin-bottom: var(--space-1);
}

.status-car-name {
  font-size: var(--font-size-title);
}

.status-refresh {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--color-text-faint);
}

.status-refresh-icon {
  flex: none;
  width: 18px;
  height: 18px;
}

.status-asof {
  color: var(--color-text-faint);
  font-size: var(--font-size-small);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.dash-hero {
  margin: var(--space-3) 0 var(--space-5);
}

.battery-percent-value {
  font-size: var(--font-size-display);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}

.dash-hero .status-range {
  color: var(--color-text-muted);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.dash-status-line {
  margin-top: var(--space-2);
  color: var(--color-text-muted);
  font-size: 15px;
  font-weight: 600;
}

/* problems / to-dos: one surface, one row per item */
.dash-health > * + * {
  border-top: 1px solid var(--color-border);
}

.dash-health > .alert {
  margin: 0;
  padding: var(--space-3) 0;
}

.dash-anomaly {
  gap: var(--space-3);
  min-height: 56px;
  margin: 0;
  padding: var(--space-2) 0;
  border-radius: 0;
  background: transparent;
  color: var(--color-danger);
  font-size: var(--font-size-body);
  font-weight: 600;
}

.dash-anomaly.is-warn,
.dash-anomaly.is-info {
  background: transparent;
  color: var(--color-text);
}

.dash-anomaly.is-info {
  font-weight: 500;
}

.dash-anomaly > .list-row-icon {
  background: var(--color-danger-soft);
  color: var(--color-danger);
}

.dash-anomaly.is-warn > .list-row-icon {
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
}

.dash-anomaly.is-info > .list-row-icon {
  background: var(--icon-chip-bg);
  color: var(--color-text-muted);
}

.dash-anomaly-code {
  margin-top: 1px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--color-text-faint);
  font-size: 12px;
  opacity: 1;
}

.dash-anomaly-fix {
  margin-top: 2px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
  font-weight: 400;
  line-height: 1.4;
  word-break: keep-all;
}

.dash-anomaly-age,
.dash-ok-detail {
  color: var(--color-text-faint);
  font-size: var(--font-size-small);
  font-weight: 500;
  opacity: 1;
}

.dash-todo {
  gap: var(--space-3);
  min-height: 52px;
  border-bottom: 0;
  font-size: 15px;
  font-weight: 600;
}

/* to-do marker: a chip-wide slot so its text lines up with the alert rows */
.dash-todo-dot {
  position: relative;
  width: var(--icon-chip);
  height: 8px;
  border-radius: 0;
  background: transparent;
}

.dash-todo-dot::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 8px;
  height: 8px;
  margin-left: -4px;
  border-radius: 50%;
  background: var(--color-primary);
}

.dash-todo.is-urgent .dash-todo-dot {
  background: transparent;
}

.dash-todo.is-urgent .dash-todo-dot::before {
  background: var(--color-danger);
}

.dash-ok {
  min-height: 48px;
  color: var(--color-text-muted);
  font-size: var(--font-size-small);
}

/* the car photo floats on the page, between problems and controls */
.car-view {
  margin: var(--space-4) auto var(--space-4);
}

.car-badge {
  background: var(--color-surface);
}

/* charge limit control */
.dash-gauge {
  padding: var(--space-4);
}

.dash-gauge .charge-limit {
  margin-top: 0;
}

.dash-limit-caption {
  margin-bottom: var(--space-2);
  color: var(--color-text);
  font-size: var(--font-size-body);
  font-weight: 600;
}

.dash-limit-caption b {
  font-weight: 800;
}

.dash-line {
  color: var(--color-text-muted);
}

/* precondition */
.precond {
  padding: var(--space-4);
  border-top: 0;
}

.precond-head {
  margin-bottom: var(--space-1);
}

.precond-line {
  color: var(--color-text-muted);
  font-size: 15px;
}

.precond-line.is-faint {
  color: var(--color-text-faint);
}

.precond-block {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

.precond-sub {
  color: var(--color-text-faint);
  font-size: var(--font-size-small);
  font-weight: 600;
}

.precond-when {
  font-size: var(--font-size-body);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.precond-chip.is-scheduled {
  background: color-mix(in srgb, #3b82f6 14%, var(--color-surface));
}

.precond-quick {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.precond-quick .btn,
.precond-picker .btn {
  min-width: 0;
  padding: 0 var(--space-1);
}

.precond-days .btn {
  min-width: 64px;
}

/* menu list */
.menu-list {
  border-top: 0;
}

.menu-row {
  gap: var(--space-3);
  min-height: 60px;
  border-bottom: 1px solid var(--color-border);
}

.menu-list > :last-child,
.menu-list > :last-child > .menu-row,
.menu-list > .menu-row-group:last-child .menu-row-panel {
  border-bottom: 0;
}

.menu-row-title {
  font-size: var(--font-size-body);
  font-weight: 600;
}

.menu-row-summary {
  margin-top: 1px;
  color: var(--color-text-muted);
}

.menu-row-panel,
.menu-row-extra {
  padding-left: calc(var(--icon-chip) + var(--space-3));
}

/* ---- period controls (운행 · 관리) ---- */
.period-text-tabs {
  display: inline-flex;
  gap: 2px;
  margin: 0 0 var(--space-2);
  padding: 3px;
  border-radius: var(--radius-md);
  background: var(--color-fill);
}

.period-text-tab {
  min-width: 56px;
  min-height: 38px;
  padding: 0 var(--space-3);
  border-radius: 9px;
  color: var(--color-text-muted);
  font-size: 15px;
  font-weight: 600;
}

.period-text-tab.is-on {
  background: var(--color-surface);
  color: var(--color-text);
  font-weight: 700;
  box-shadow: 0 1px 3px rgba(25, 31, 40, 0.12);
}

.period-text-tab.is-on::after {
  display: none;
}

.period-nav-label {
  font-size: var(--font-size-section);
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* ---- hero totals (관리 · 충전) ---- */
.charge-hero,
.manage-hero {
  margin: var(--space-2) 0 var(--space-3);
  padding: var(--space-5) var(--space-4);
}

.summary-hero-label {
  color: var(--color-text-muted);
  font-size: 15px;
  font-weight: 600;
}

.charge-hero-value,
.manage-hero-value {
  margin-top: var(--space-1);
  font-size: var(--font-size-hero);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.03em;
}

.charge-hero-sub,
.manage-hero-sub {
  margin-top: var(--space-2);
  font-size: var(--font-size-body);
}

.charge-hero-note {
  margin-top: 2px;
  color: var(--color-text-faint);
}

.manage-gasoline {
  padding: var(--space-4);
}

/* battery gauge = footnote under the cost hero, on the page */
.battery-gauge {
  margin: 0 var(--space-1) var(--space-5);
}

.manage-gasoline-title {
  margin-bottom: var(--space-2);
}

/* ---- 운행 timeline: one surface per day ---- */
.card.trip-list {
  padding: 0;
  background: transparent;
}

.tl-day {
  padding: 0 var(--space-4) var(--space-2);
}

.tl-day + .tl-day {
  margin-top: 0;
}

.tl-day-header {
  min-height: 52px;
  margin: 0 calc(var(--space-4) * -1);
  padding: 0 var(--space-4);
  border-radius: var(--radius-group) var(--radius-group) 0 0;
  background: var(--color-surface);
}

.tl-day-title {
  font-size: var(--font-size-body);
  font-weight: 700;
}

.tl-dot {
  background: var(--color-surface);
}

.tl-seg-panel .seg,
.tl-seg-panel .input-field {
  background: var(--color-surface);
}

.trip-summary {
  margin-bottom: var(--space-5);
}

/* ---- 설정 ---- */
.settings-home-row {
  min-height: 64px;
}

.settings-home-row .list-row-label > span {
  font-weight: 600;
}

.settings-home-row .list-row-sub {
  font-weight: 400;
}

.settings-back {
  color: var(--color-text-muted);
  font-weight: 600;
}

/* ---- dark ---- */
@media (prefers-color-scheme: dark) {
  .period-text-tab.is-on {
    background: var(--color-bg-sunken);
    box-shadow: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bottom-tab-bar .tab-link-icon {
    transition: none;
  }
}

/* selected filter row in 관리 분류별: the chip carries the active state */
.manage-cat.is-selected .list-row-label > .list-row-icon {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}
