/*
 * ==================================================================
 * Sealion Remote — visual design
 * ==================================================================
 *
 * Hallmark redesign · genre: modern-minimal × atmospheric
 * Voice: "Precision Instrument".
 *
 * The design premise: BYD photographs the Sealion 7 on a mathematically
 * pure white studio card, so the interface IS that card, and the controls
 * are milled into it. Official photography composites invisibly with
 * `mix-blend-mode: multiply`; the type is a Cabinet Grotesk / Geist
 * pairing with Geist Mono reserved for machine identifiers and live
 * telemetry; and exactly one accent — BYD's own blue — marks state.
 *
 * ------------------------------------------------------------------
 * Pre-emit self-critique (6 axes)
 * ------------------------------------------------------------------
 * 1. Typography — Cabinet Grotesk 700/800 display against Geist 300–600
 *    body is a 300+ unit weight gap and two different skeletons, so
 *    hierarchy comes from the faces rather than size steps. Geist Mono
 *    tags exactly two roles (VIN, telemetry). Zen Kaku Gothic New gives
 *    the Japanese its own designed weight instead of an OS fallback.
 * 2. Colour — one accent held under 3% of any viewport; every neutral is
 *    tinted toward hue 255 so nothing reads as dead grey. Pure #ffffff
 *    survives ONLY under multiply-blended photography.
 * 3. Layout — a 12-column control grid with deliberately unequal spans,
 *    stacked section heads (never eyebrow-left / heading-right), and an
 *    asymmetric login split. No identical-card triplets.
 * 4. Depth — two shadows (whisper, hairline), never stacked. Elevation is
 *    carried by weight and scale, which is the only honest option on a
 *    white page.
 * 5. Motion — one easing curve, three durations, and a real
 *    reduced-motion answer for every animation rather than a blanket
 *    duration clamp.
 * 6. Restraint — no gradient text, no purple/cyan anything, no third
 *    accent, one frosted surface (the sticky bar that needs it), and no
 *    icon-above-heading-above-copy card grid.
 *
 * ------------------------------------------------------------------
 * ⚠️ THE PURE-WHITE CONSTRAINT — do not "improve" this
 * ------------------------------------------------------------------
 * `.splash`, `.hero-card`, `.login-hero` and `.run-strip` must stay
 * exactly `var(--paper-pure)` (#ffffff). Those surfaces carry official
 * photography blended with `multiply`, which only dissolves the photo's
 * white studio card if the backdrop is precisely (255,255,255).
 *
 *   - Any tint behind such a photo makes its white render LIGHTER than
 *     the surround — a visible rectangle.
 *   - `drop-shadow` is equally forbidden there: the source is an opaque
 *     rectangle, not an alpha cut-out, so the shadow traces the photo's
 *     edge and draws exactly the box being hidden.
 *
 * Tokens live in `tokens.css`, loaded before this file.
 */


/* ------------------------------------------------------------------ */
/* BYD brand marks                                                     */
/* ------------------------------------------------------------------ */

/* The wordmark is an inline SVG that inherits currentColor, so it can be
   dropped anywhere and picks up the surrounding text colour. */
.byd-mark {
  display: inline-block;
  vertical-align: middle;
  line-height: 0;
  color: var(--white);
}

.byd-mark svg {
  display: block;
  width: 100%;
  height: auto;
  fill: currentColor;
}

.byd-mark--xs {
  width: 30px;
  opacity: 0.55;
}

.byd-mark--sm {
  width: 42px;
}

.byd-mark--lg {
  width: 82px;
}

/* Faint repeating wordmark used as a texture behind panels. */
.byd-watermark {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.05;
  color: var(--white);
  display: flex;
  flex-wrap: wrap;
  gap: 26px;
  padding: 18px;
  overflow: hidden;
  /* Spread the marks over the full panel instead of bunching them at the
     top, which left an obvious empty half below the lockup. The radial mask
     then hollows out the middle so no mark ever sits behind the form copy --
     the brand field reads as a border pattern framing the card. */
  align-content: space-evenly;
  justify-content: center;
  /*
   * The hollow has to clear the WHOLE card, not just its middle: at 46% the
   * marks were landing on top of the legal paragraph, which made the small
   * type unreadable. Pushed out to 72%, so the field reads as a frame
   * around the form rather than a texture behind it.
   */
  -webkit-mask-image: radial-gradient(ellipse closest-side at 50% 50%, transparent 72%, #000 100%);
  mask-image: radial-gradient(ellipse closest-side at 50% 50%, transparent 72%, #000 100%);
}

.byd-watermark .byd-mark {
  width: 34px;
  opacity: 1;
}

/* A small "BYD" chip used to sprinkle the brand through the layout. */
.byd-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--white-12);
  background: var(--white-06);
  color: var(--text-dim);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
}

.byd-chip .byd-mark {
  width: 26px;
  color: var(--white);
  opacity: 0.9;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  /*
   * `clip`, not `hidden`: it stops the decorative overflow (the drive-past
   * car, the full-bleed rules) from ever scrolling the document sideways,
   * while leaving `position: sticky` on the topbar working. `hidden` would
   * create a new scroll container and break it.
   */
  overflow-x: clip;
}

/*
 * Tells the browser to render its own chrome light: the select dropdown
 * panel, scrollbars, autofill highlight and the caret. Without this, native
 * widgets stay dark and stick out badly against the white UI.
 */
html {
  color-scheme: light;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 400;
  line-height: 1.6;
  letter-spacing: -0.006em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
  /* Room for the fixed bottom PIN bar on mobile. */
  padding-bottom: env(safe-area-inset-bottom);
}

/*
 * Display type. Cabinet Grotesk is a much narrower, higher-contrast
 * skeleton than Geist, so headings separate from body copy by *shape*
 * before size even enters into it.
 */
h1,
h2,
h3,
.u-display {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.1;
}

button,
input,
select {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

/* Utility */
.hidden {
  display: none !important;
}

/*
 * The mono outlier, carrying exactly two roles: machine identifiers (VIN,
 * plate) and live telemetry (%, km, °C). Nothing else gets it — a third
 * role would make it a second body font, which is slop.
 */
.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
  letter-spacing: -0.01em;
}

/* A hairline rule that spans wider than its column — one of the two
   deliberate grid-breaks in the layout. */
.u-rule {
  height: 1px;
  border: 0;
  margin: 0;
  background: linear-gradient(90deg, var(--line) 0%, var(--line) 62%, transparent 100%);
}

/* ------------------------------------------------------------------ */
/* Login                                                               */
/* ------------------------------------------------------------------ */

/*
 * Deliberately unequal split, biased hard to the image side. A 50/50 or
 * even a 1.15/1 split reads as a default; 1.42/1 reads as a decision, and
 * it gives the car enough room to be a photograph rather than a thumbnail.
 */
.login-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1.42fr 1fr;
}

.login-hero {
  position: relative;
  overflow: hidden;
  /*
   * Mathematically PURE white -- not off-white, not tinted. The hero photo
   * is composited with `multiply`, which only makes its studio card vanish
   * if the backdrop is exactly (255,255,255). Put any wash behind it and
   * the card renders LIGHTER than its surround, i.e. a visible rectangle.
   * Depth comes from the car's own drop shadow instead.
   */
  background: var(--paper-pure);
  display: grid;
  /*
   * Three explicit rows rather than `space-between`: lockup, then the car's
   * own band, then the headline block. `space-between` left the lockup
   * floating in the middle of a large empty top region because the car is
   * absolutely positioned and contributes no height.
   */
  grid-template-rows: auto 1fr auto;
  /* Generous top, tighter bottom: the headline wants to sit low and heavy,
     so the rhythm is deliberately uneven rather than uniformly padded. */
  padding: var(--space-lg) var(--space-xl) var(--space-lg);
}

/*
 * A single hairline sitting just under the wordmark — the one horizontal
 * reference line on the panel, so the car reads as resting on a measured
 * surface rather than floating in a void. It runs past the left edge and
 * fades out to the right: the layout's one deliberate grid-break.
 */
.login-hero::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 88px;
  height: 1px;
  background: linear-gradient(90deg, var(--line) 0%, transparent 72%);
  pointer-events: none;
}

.login-hero__img {
  position: absolute;
  /*
   * Confined to a middle band, NOT the whole panel: the headline and lede
   * live in the lower third, and a car spanning the full height would run
   * straight through them.
   */
  left: 0;
  right: 0;
  /*
   * A middle band with an EXPLICIT height. The panel is a space-between
   * column: wordmark pinned top, headline pinned bottom, so the car gets
   * the space in between. (`height: auto` here would override `bottom` and
   * let the photo blow up to its intrinsic size, spilling over both.)
   */
  /*
   * Sits BELOW the wordmark lockup and ABOVE the headline. Earlier this
   * band started at 24% and ran through the lockup, hiding the "UNOFFICIAL
   * CLIENT" line behind the bonnet.
   */
  top: 27%;
  height: 34%;
  width: 100%;
  object-fit: contain;
  /* The official configurator shot sits on a pure-white card, so `multiply`
     makes that card vanish into this white panel and only the bodywork
     remains -- no photo edge anywhere. */
  object-position: center 72%;
  mix-blend-mode: multiply;
}

.login-hero__scrim {
  position: absolute;
  inset: 0;
  /*
   * Two-ended fade to pure white: the top keeps the wordmark legible, and
   * the bottom guarantees clean white under the headline/lede even on a
   * short viewport where the car creeps down into them.
   */
  background:
    linear-gradient(
      180deg,
      rgba(255, 255, 255, 0.9) 0%,
      rgba(255, 255, 255, 0.35) 18%,
      rgba(255, 255, 255, 0) 34%
    ),
    linear-gradient(
      0deg,
      rgba(255, 255, 255, 1) 0%,
      rgba(255, 255, 255, 0.96) 22%,
      rgba(255, 255, 255, 0) 38%
    );
}

.login-hero__top,
.login-hero__bottom {
  position: relative;
  z-index: 1;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Holds the BYD wordmark — wider than tall, so it reads as a badge. */
.brand__mark {
  height: 34px;
  padding: 0 var(--space-xs);
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  /* Ink-filled badge: the wordmark reverses out to true white, which is the
     only place pure white appears as *type* on a white page -- so it pops
     without needing a colour. Flat fill, not a gradient: a two-stop
     gradient on a 34px chip is decoration nobody can see. */
  background: var(--text);
  box-shadow: var(--shadow-hairline);
}

.brand__mark .byd-mark {
  width: 36px;
  color: var(--on-ink);
}

.brand__name {
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: 800;
  letter-spacing: 0.03em;
  display: block;
}

.brand__sub {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-faint);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  display: block;
}

/*
 * The one true display moment in the app. Cabinet Grotesk 800 at display
 * size, tightly tracked, with the second line carrying the weight — the
 * headline is 18 characters per line, well inside the ≤50-char bucket that
 * earns full display size.
 */
.login-hero__title {
  font-family: var(--font-display);
  font-size: var(--text-display);
  line-height: 1.02;
  font-weight: 800;
  letter-spacing: -0.038em;
  margin: 0 0 var(--space-sm);
  /* Balanced wrapping keeps the two lines close in length instead of
     leaving one orphan word on line two. */
  text-wrap: balance;
}

/*
 * The de-emphasised half of the headline. Light weight against the 800
 * gives a 500-unit contrast inside a single sentence, which is what makes
 * the emphasis read as typographic rather than as a colour change.
 */
.login-hero__title em {
  font-style: normal;
  font-weight: 300;
  color: var(--text-dim);
  letter-spacing: -0.03em;
}

.login-hero__lede {
  max-width: 42ch;
  color: var(--text-dim);
  margin: 0 0 var(--space-lg);
  font-size: var(--text-sm);
  line-height: 1.62;
}

/*
 * Capability strip. Three items, but NOT three identical cards: they sit on
 * a single hairline with mono labels beneath display-weight values, so the
 * strip reads as an instrument legend rather than a feature grid.
 */
.hero-specs {
  display: flex;
  gap: var(--space-xl);
  flex-wrap: wrap;
  padding-top: var(--space-md);
  border-top: 1px solid var(--line);
}

.hero-spec__value {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
  line-height: 1.2;
}

.hero-spec__label {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-faint);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.login-panel {
  position: relative; /* anchors the BYD watermark field */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-xl) var(--space-lg);
  background: var(--surface);
  border-left: 1px solid var(--line-soft);
  overflow: hidden;
}

.login-card {
  position: relative; /* above the watermark */
  z-index: var(--z-base);
  width: 100%;
  max-width: 372px;
}

.login-card h2 {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 800;
  margin: 0 0 var(--space-3xs);
  letter-spacing: -0.03em;
}

.login-card__hint {
  color: var(--text-dim);
  font-size: var(--text-xs);
  margin: 0 0 var(--space-lg);
}

.field {
  margin-bottom: var(--space-md);
}

/*
 * Mono field labels. Uppercase mono at 10px is the register of an
 * instrument panel legend, and it separates cleanly from the Geist input
 * text underneath without needing a weight or colour jump.
 */
.field label {
  display: block;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: var(--space-xs);
}

.field input,
.field select {
  width: 100%;
  padding: var(--space-sm) var(--space-md);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
    background var(--dur) var(--ease);
}

.field input:hover:not(:focus),
.field select:hover:not(:focus) {
  border-color: var(--graphite);
}

/* The accent's primary job: marking focus. */
.field input:focus,
.field select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
  background: var(--paper-pure);
}

.field input::placeholder {
  color: var(--text-faint);
}

.field__note {
  font-size: var(--text-xs);
  color: var(--text-faint);
  margin-top: var(--space-2xs);
  line-height: 1.55;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 500;
  letter-spacing: -0.005em;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
    transform var(--dur-fast) var(--ease), box-shadow var(--dur) var(--ease);
}

.btn:hover:not(:disabled) {
  background: var(--surface-3);
  border-color: var(--graphite);
}

.btn:active:not(:disabled) {
  transform: translateY(1px);
}

.btn:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-color: var(--accent);
}

.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/*
 * Ink-filled primary action: on a white page the strongest possible mark is
 * the darkest one, so the roles of white and ink simply swapped. Flat fill
 * rather than a gradient — the shadow does the lifting.
 */
.btn--primary {
  background: var(--text);
  border-color: var(--text);
  color: var(--on-ink);
  font-weight: 600;
  box-shadow: var(--shadow-whisper);
}

.btn--primary:hover:not(:disabled) {
  background: var(--platinum);
  border-color: var(--platinum);
}

.btn--block {
  width: 100%;
  padding-block: var(--space-sm);
}

.btn--danger {
  color: var(--danger);
  border-color: color-mix(in oklch, var(--danger) 34%, transparent);
}

.btn--danger:hover:not(:disabled) {
  background: var(--danger-soft);
  border-color: color-mix(in oklch, var(--danger) 50%, transparent);
}

.alert {
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  margin-bottom: var(--space-md);
  border: 1px solid;
  /* An icon-free alert still needs a non-colour signal for anyone who
     can't separate the red, so it gets a heavy left edge. */
  border-left-width: 3px;
}

.alert--error {
  background: var(--danger-soft);
  border-color: color-mix(in oklch, var(--danger) 34%, transparent);
  border-left-color: var(--danger);
  color: oklch(42% 0.15 22);
}

.legal {
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px solid var(--line-soft);
  font-size: var(--text-2xs);
  color: var(--text-faint);
  line-height: 1.7;
  max-width: 46ch;
}

/* ------------------------------------------------------------------ */
/* App chrome                                                          */
/* ------------------------------------------------------------------ */

/*
 * N1b — the dense three-section bar: brand lockup left, live status
 * centre, vehicle picker + actions right. The centre slot is what stops
 * this being the reflexive two-element AI navbar: it carries real state
 * (connection, last sync) that the user would otherwise have to hunt for.
 *
 * This is the ONE frosted surface in the app. It earns it: content scrolls
 * underneath, so an opaque bar would sever the page.
 */
.topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-lg);
  background: color-mix(in oklch, var(--bg) 82%, transparent);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid var(--line-soft);
}

/* Centre slot: a mono status readout, hidden on phones where the bar has
   no room for it. */
.topbar__status {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
  white-space: nowrap;
  min-width: 0;
}

/* A 5px live dot that breathes, so "connected" is legible at a glance. */
.topbar__dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ok);
  flex: none;
  animation: pulse-dot 2.4s var(--ease) infinite;
}

@keyframes pulse-dot {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.4;
    transform: scale(0.82);
  }
}

.topbar__right {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.vehicle-select {
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  max-width: 220px;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.vehicle-select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.icon-btn {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--surface-2);
  cursor: pointer;
  color: var(--text-dim);
  font-size: var(--text-xs);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}

.icon-btn:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-color: var(--accent);
}

.icon-btn:hover {
  background: var(--surface-3);
  color: var(--text);
}

.page {
  max-width: 1140px;
  margin: 0 auto;
  /* Uneven: tight at the top (the topbar already spends vertical room),
     generous at the bottom to clear the docked PIN bar. */
  padding: var(--space-lg) var(--space-lg) var(--space-3xl);
}

/* ------------------------------------------------------------------ */
/* Vehicle hero card                                                   */
/* ------------------------------------------------------------------ */

.hero-card {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  /*
   * Pure white, no wash: the photo above is `multiply`-blended, and any
   * tint behind it would make its white studio card show up as a lighter
   * rectangle. The card reads as a surface through its border and shadow.
   */
  background: var(--paper-pure);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-whisper);
  margin-bottom: var(--space-lg);
}

.hero-card__img {
  display: block;
  width: 100%;
  height: 300px;
  /* `contain` + `multiply` keeps the whole car visible and dissolves the
     photo's white studio card into this white one. */
  object-fit: contain;
  object-position: center 76%;
  mix-blend-mode: multiply;
}

.hero-card__scrim {
  position: absolute;
  inset: 0;
  /* Guarantees clean white behind the name/VIN and the stat row, whatever
     the photo does underneath. */
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.92) 0%, rgba(255, 255, 255, 0) 34%),
    linear-gradient(0deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0) 26%);
}

.hero-card__body {
  position: absolute;
  inset: 0;
  padding: 22px 24px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.hero-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
}

/* On narrow screens the status pills wrap onto several rows and used to
   overlap the VIN, so stack the head vertically instead of side by side. */
@media (max-width: 600px) {
  .hero-card__head {
    flex-direction: column;
    gap: 10px;
  }

  .hero-card__head .pills {
    justify-content: flex-start;
  }
}

/* Ink wordmark badge over the bodywork — the "precious" accent, inverted
   for the white theme so it still reads as the strongest mark on screen. */
.hero-card__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-2xs) var(--space-sm) var(--space-2xs) var(--space-xs);
  margin-bottom: var(--space-xs);
  border-radius: var(--radius-pill);
  background: var(--text);
}

.hero-card__badge .byd-mark {
  width: 28px;
  color: var(--on-ink);
  opacity: 1;
}

.hero-card__badge span {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.22em;
  color: var(--on-ink);
}

.hero-card__name {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 800;
  letter-spacing: -0.03em;
  margin: 0;
}

/*
 * The VIN is a machine identifier, so it gets the mono outlier. That is
 * role one of two for the face — telemetry below is role two.
 */
.hero-card__vin {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--text-dim);
  letter-spacing: 0.06em;
  /* A white halo, not a black one, so the VIN survives falling over dark
     bodywork on the photo underneath. */
  text-shadow: 0 1px 6px oklch(100% 0 0 / 0.92);
}

/* Battery / range readout overlaid on the photo. */
.hero-stats {
  display: flex;
  align-items: flex-end;
  gap: var(--space-xl);
  flex-wrap: wrap;
}

/*
 * The single largest figure in the app, and the reason the mono face is in
 * the stack at all: a state-of-charge percentage in tabular mono at 39px
 * reads as an instrument, whereas the same number in the body sans reads
 * as marketing copy.
 */
.hero-stat__value {
  font-family: var(--font-mono);
  font-size: var(--text-2xl);
  font-weight: 400;
  letter-spacing: -0.04em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.hero-stat__value span {
  font-size: var(--text-sm);
  color: var(--text-dim);
  margin-left: var(--space-3xs);
  letter-spacing: 0;
}

.hero-stat__label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-top: var(--space-2xs);
}

.battery-bar {
  width: 100%;
  max-width: 300px;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--graphite);
  overflow: hidden;
  margin-top: var(--space-sm);
}

/*
 * Flat fill, no gradient. A 3px bar is far too small for a gradient to
 * register as anything but noise, and a solid state colour is easier to
 * read at a glance — which is the entire job of a charge indicator.
 */
.battery-bar__fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--ok);
  transition: width var(--dur-slow) var(--ease), background var(--dur) var(--ease);
}

.battery-bar__fill--low {
  background: var(--danger);
}

/* Status pills */
.pills {
  display: flex;
  gap: 7px;
  flex-wrap: wrap;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 99px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  border: 1px solid var(--line);
  /* Was an opaque near-black left over from the dark theme, which made the
     default pill unreadable on white. */
  background: var(--surface-2);
  color: var(--text-dim);
}

.pill--ok {
  color: var(--ok);
  border-color: color-mix(in oklch, var(--ok) 34%, transparent);
  background: var(--ok-soft);
}

.pill--warn {
  color: var(--warn);
  border-color: color-mix(in oklch, var(--warn) 34%, transparent);
}

.pill--danger {
  color: var(--danger);
  border-color: color-mix(in oklch, var(--danger) 34%, transparent);
  background: var(--danger-soft);
}

.pill--cool {
  color: var(--cool);
  border-color: color-mix(in oklch, var(--cool) 34%, transparent);
  background: var(--cool-soft);
}

.pill--heat {
  color: var(--heat);
  border-color: color-mix(in oklch, var(--heat) 34%, transparent);
  background: var(--heat-soft);
}

/* ------------------------------------------------------------------ */
/* Sections & cards                                                    */
/* ------------------------------------------------------------------ */

/*
 * Deliberately uneven section rhythm. Every section having identical
 * margins is what makes a page read as a template, so the first section
 * after the hero sits closer and later ones breathe more (see
 * `.section + .section` below).
 */
.section {
  margin-bottom: var(--space-lg);
}

.section + .section {
  margin-top: var(--space-xl);
}

/*
 * STACKED head — never eyebrow-left / heading-right. That two-column
 * editorial head is the single most reliable templated-AI tell, and it also
 * squeezes long Japanese notes into unreadable wraps.
 */
.section__head {
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
  padding-bottom: var(--space-xs);
  border-bottom: 1px solid var(--line-soft);
}

/*
 * Section titles are display-face, not uppercase mono: at 20px Cabinet
 * Grotesk 700 they act as real headings in the document outline, and the
 * mono register stays reserved for labels and telemetry.
 */
.section__title {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 700;
  letter-spacing: -0.028em;
  text-transform: none;
  color: var(--text);
  margin: 0;
  white-space: nowrap;
}

/* Long Japanese notes must not squeeze the title into a two-line wrap. */
@media (max-width: 600px) {
  .section__head {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
}

.section__note {
  font-size: var(--text-xs);
  color: var(--text-faint);
  margin-left: auto; /* keeps the note trailing after the title + chip */
  text-align: right;
}

.card {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  /* Card padding is deliberately smaller than the page padding, so the
     nesting rhythm steps rather than repeating one value. */
  padding: var(--space-md);
}

.grid {
  display: grid;
  gap: var(--space-sm);
}

.grid--cols2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/*
 * A true 12-column grid rather than `auto-fill`. Auto-fill produces
 * identically-sized cells, which is precisely the uniform feature grid
 * that reads as generated. Here the children take deliberately unequal
 * spans (see `.tile:first-child` / `:nth-child(4n)` below), so the block
 * has a composition instead of a repeat.
 */
.grid--auto {
  grid-template-columns: repeat(12, minmax(0, 1fr));
}

.grid--auto > * {
  grid-column: span 3;
}

/* The first control in a group is the one people reach for most, so it
   gets a wider cell and reads as the primary. */
.grid--auto > *:first-child {
  grid-column: span 4;
}

/* …and one later cell absorbs the remainder so no row ends ragged. */
.grid--auto > *:nth-child(5) {
  grid-column: span 5;
}

.grid--macros {
  grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
}

/* ------------------------------------------------------------------ */
/* Control tiles                                                       */
/* ------------------------------------------------------------------ */

/*
 * The control tile. Deliberately NOT the icon-above-heading-above-copy
 * triplet: the icon sits inline with the label on one row and the sub-copy
 * runs full width beneath, so the tile reads as a labelled switch rather
 * than as a marketing feature card.
 */
.tile {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    'icon label'
    'sub  sub';
  align-items: center;
  gap: var(--space-xs) var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface-2);
  cursor: pointer;
  text-align: left;
  overflow: hidden;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
    transform var(--dur-fast) var(--ease);
}

/*
 * A hairline that wipes in from the left on hover — the tile's own
 * affordance, drawn in the accent so hovering feels like arming a control.
 * Cheaper and calmer than moving the whole tile.
 */
.tile::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: center;
  transition: transform var(--dur) var(--ease);
}

.tile:hover:not(:disabled) {
  border-color: var(--graphite);
  box-shadow: var(--shadow-whisper);
  transform: translateY(-1px);
}

.tile:hover:not(:disabled)::before {
  transform: scaleY(1);
}

.tile:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.tile:active:not(:disabled) {
  transform: translateY(0);
}

.tile:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.tile__icon {
  grid-area: icon;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  background: var(--surface-3);
  color: var(--platinum);
  font-size: var(--text-xs);
  flex: none;
}

.tile__label {
  grid-area: label;
  font-weight: 500;
  font-size: var(--text-sm);
  letter-spacing: -0.01em;
}

.tile__sub {
  grid-area: sub;
  font-size: var(--text-2xs);
  color: var(--text-faint);
  line-height: 1.5;
}

.tile--cool .tile__icon {
  background: var(--cool-soft);
  color: var(--cool);
}

.tile--heat .tile__icon {
  background: var(--heat-soft);
  color: var(--heat);
}

.tile--danger .tile__icon {
  background: var(--danger-soft);
  color: var(--danger);
}

.tile--ok .tile__icon {
  background: var(--ok-soft);
  color: var(--ok);
}

/* Macro tiles get a wider body for their description. */
.tile--macro {
  gap: 8px;
}

.tile__steps {
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}

/* ------------------------------------------------------------------ */
/* Seat climate control                                                */
/* ------------------------------------------------------------------ */

.seat-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 13px 0;
  border-bottom: 1px solid var(--line-soft);
}

.seat-row:last-child {
  border-bottom: none;
}

.seat-row__info {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.seat-row__icon {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  font-size: 14px;
}

.seat-row__icon--heat {
  background: var(--heat-soft);
  color: var(--heat);
}

.seat-row__icon--vent {
  background: var(--cool-soft);
  color: var(--cool);
}

.seat-row__name {
  font-weight: 600;
  font-size: 13.5px;
}

.seat-row__state {
  font-size: 11.5px;
  color: var(--text-faint);
}

.seat-row__unsupported {
  font-size: 11.5px;
  color: var(--text-faint);
  font-style: italic;
}

/* Three-position OFF / 弱 / 強 selector. */
.level-group {
  display: inline-flex;
  padding: 3px;
  gap: 3px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 11px;
  flex-shrink: 0;
}

.level-btn {
  min-width: 46px;
  padding: 6px 10px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
  transition: background 0.15s, color 0.15s;
}

.level-btn:hover:not(:disabled):not(.is-active) {
  background: var(--white-06);
  color: var(--text);
}

.level-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.level-btn.is-active {
  background: var(--surface-3);
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--line);
}

/* Active states pick up the functional colour. */
.level-group--heat .level-btn.is-active:not([data-level='off']) {
  background: var(--heat);
  color: var(--on-ink);
  box-shadow: none;
}

.level-group--vent .level-btn.is-active:not([data-level='off']) {
  background: var(--cool);
  color: var(--on-ink);
  box-shadow: none;
}

/* ------------------------------------------------------------------ */
/* Climate settings                                                    */
/* ------------------------------------------------------------------ */

.setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  padding: 12px 0;
  border-bottom: 1px solid var(--line-soft);
}

.setting-row:last-child {
  border-bottom: none;
}

.setting-row__label {
  font-size: 13px;
  font-weight: 600;
}

.setting-row__hint {
  font-size: 11.5px;
  color: var(--text-faint);
}

.stepper {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 11px;
  padding: 3px;
}

.stepper button {
  width: 34px;
  height: 32px;
  border: none;
  background: transparent;
  border-radius: 8px;
  color: var(--text-dim);
  cursor: pointer;
  font-size: 14px;
  transition: background 0.15s, color 0.15s;
}

.stepper button:hover:not(:disabled) {
  background: var(--white-06);
  color: var(--text);
}

.stepper button:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.stepper__value {
  min-width: 62px;
  text-align: center;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ */
/* Detail readouts                                                     */
/* ------------------------------------------------------------------ */

.readouts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line-soft);
}

.readout {
  background: var(--surface);
  padding: 14px;
}

.readout__label {
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 5px;
}

.readout__value {
  font-size: 17px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.readout__value--muted {
  color: var(--text-faint);
  font-weight: 400;
}

/* ------------------------------------------------------------------ */
/* PIN bar (docked)                                                    */
/* ------------------------------------------------------------------ */

/*
 * Docked PIN bar. Frosted like the topbar for the same reason — content
 * scrolls under it — and using the light `--bg` mix, not the near-black
 * left over from the dark theme.
 */
.pinbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-lg) calc(var(--space-sm) + env(safe-area-inset-bottom));
  background: color-mix(in oklch, var(--bg) 88%, transparent);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-top: 1px solid var(--line);
}

.pinbar__label {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
  white-space: nowrap;
}

.pinbar__input {
  width: 128px;
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  letter-spacing: 0.34em;
  font-variant-numeric: tabular-nums;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.pinbar__input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

/*
 * Shown in place of the input once the PIN lives on the server. Green, so
 * "no field to fill in" reads as a completed state rather than as something
 * missing from the bar.
 */
.pinbar__saved {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 5px 10px;
  border-radius: 7px;
  background: var(--ok-soft);
  color: var(--ok);
  font-size: 11.5px;
  font-weight: 700;
}

.pinbar__note {
  font-size: 11.5px;
  color: var(--text-faint);
  line-height: 1.4;
}

.pinbar__spacer {
  flex: 1;
}

/* ------------------------------------------------------------------ */
/* Footer — Ft1 mast-headed                                            */
/* ------------------------------------------------------------------ */

/*
 * A single horizontal band: wordmark and tagline anchor the left, a mono
 * colophon runs the right. Deliberately NOT the four-column
 * Product/Company/Resources/Legal sitemap — this app has one page, so a
 * sitemap footer would be pure theatre.
 */
.appfoot {
  margin-top: var(--space-2xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--line);
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: var(--space-lg);
}

.appfoot__mast {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.appfoot__mast .byd-mark {
  width: 52px;
  color: var(--text);
}

.appfoot__name {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 800;
  letter-spacing: -0.03em;
  display: block;
  line-height: 1.1;
}

.appfoot__tagline {
  font-size: var(--text-xs);
  color: var(--text-dim);
  display: block;
}

/* The colophon: build facts in mono, ragged-right, no invented metrics. */
.appfoot__colophon {
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.9;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  text-align: right;
}

.appfoot__colophon span {
  display: block;
}

/* ------------------------------------------------------------------ */
/* Toasts                                                              */
/* ------------------------------------------------------------------ */

.toasts {
  position: fixed;
  top: 68px;
  right: 20px;
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: 9px;
  max-width: min(380px, calc(100vw - 40px));
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  font-size: 13px;
  animation: toast-in 0.22s ease;
}

.toast--ok {
  border-color: color-mix(in oklch, var(--ok) 34%, transparent);
}

.toast--ok .toast__icon {
  color: var(--ok);
}

.toast--error {
  border-color: color-mix(in oklch, var(--danger) 34%, transparent);
}

.toast--error .toast__icon {
  color: var(--danger);
}

.toast--info .toast__icon {
  color: var(--cool);
}

.toast__body {
  flex: 1;
  min-width: 0;
}

.toast__steps {
  margin: 6px 0 0;
  padding-left: 16px;
  font-size: 12px;
  color: var(--text-dim);
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateX(14px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* Spinner used on busy tiles/buttons. */
.spin {
  animation: spin 0.9s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Skeleton loading state */
.skeleton {
  border-radius: var(--radius);
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite;
}

@keyframes shimmer {
  to {
    background-position: -200% 0;
  }
}

/* ------------------------------------------------------------------ */
/* Empty / centred states                                              */
/* ------------------------------------------------------------------ */

.center-state {
  min-height: 60vh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 40px 20px;
}

.center-state__icon {
  font-size: 30px;
  color: var(--text-faint);
  margin-bottom: 14px;
}

.center-state h3 {
  margin: 0 0 8px;
  font-weight: 600;
}

.center-state p {
  margin: 0 0 18px;
  color: var(--text-dim);
  max-width: 44ch;
}

/* ------------------------------------------------------------------ */
/* Startup splash — the Sealion 7 sweeps through, then hands over      */
/* ------------------------------------------------------------------ */

/*
 * The splash lives in the document from the very first byte (see index.tsx)
 * so it paints before app.js has even parsed — that is the whole point of a
 * splash. It removes itself: `splash-out` ends in opacity 0 /
 * visibility hidden, and app.js also drops the node once it is done, so a
 * failed script can never leave the UI permanently covered.
 */
.splash {
  position: fixed;
  inset: 0;
  z-index: 200;
  /*
   * Pure white and nothing else. The car sweeping across is `multiply`
   * blended, so the frame it travels over has to be exactly (255,255,255)
   * for its studio card to disappear. Depth is carried by the ink speed
   * streaks and the car's drop shadow, never by a background tint.
   */
  background: var(--paper-pure);
  display: grid;
  place-items: center;
  overflow: hidden;
  animation: splash-out 0.55s ease 2.35s forwards;
}

/* Twin light streaks suggesting speed, drawn behind the car. */
.splash__road {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 260px;
  transform: translateY(-42%);
  pointer-events: none;
}

.splash__road::before,
.splash__road::after {
  content: '';
  position: absolute;
  left: -40%;
  width: 180%;
  height: 1px;
  /* Ink streaks on white -- the dark theme's light streaks inverted. */
  background: linear-gradient(
    90deg,
    transparent,
    var(--white-20),
    var(--white-06),
    transparent
  );
  animation: streak 1.1s linear infinite;
}

.splash__road::before {
  top: 58%;
}

.splash__road::after {
  top: 72%;
  opacity: 0.5;
  animation-duration: 1.5s;
  animation-delay: 0.2s;
}

/*
 * The car photo is BYD's own configurator shot on a mathematically pure
 * WHITE card, so `multiply` blending drops its background into the page
 * white completely -- the sweep has no visible rectangle, which is what
 * makes it read as the car itself moving. (This is the exact mirror of the
 * `screen`-on-black trick the dark theme used.)
 */
.splash__car {
  position: relative;
  width: min(80vw, 660px);
  mix-blend-mode: multiply;
  /*
   * No `drop-shadow` here. The source is an opaque rectangular photo, not a
   * cut-out with alpha, so drop-shadow would trace the PHOTO's edges and
   * draw exactly the box we are trying to hide. The car's own studio
   * shadow, baked into the official shot, does the grounding.
   */
  animation: car-drive-in 1.5s cubic-bezier(0.16, 0.84, 0.24, 1) forwards;
}

.splash__car img {
  display: block;
  width: 100%;
  height: auto;
}

.splash__brand {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 12vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  opacity: 0;
  animation: splash-brand 0.7s ease 0.95s forwards;
}

.splash__brand .byd-mark {
  width: 88px;
  color: var(--text);
}

.splash__title {
  font-size: 12px;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* The car enters from the left, overshoots slightly, then settles. */
@keyframes car-drive-in {
  0% {
    transform: translateX(-135%) scale(0.9);
    opacity: 0;
  }
  38% {
    opacity: 1;
  }
  72% {
    transform: translateX(4%) scale(1.02);
  }
  100% {
    transform: translateX(0) scale(1);
    opacity: 1;
  }
}

@keyframes streak {
  0% {
    transform: translateX(-30%);
    opacity: 0;
  }
  30% {
    opacity: 1;
  }
  100% {
    transform: translateX(30%);
    opacity: 0;
  }
}

@keyframes splash-brand {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes splash-out {
  to {
    opacity: 0;
    visibility: hidden;
  }
}

/* ------------------------------------------------------------------ */
/* Home-screen drive-past strip                                        */
/* ------------------------------------------------------------------ */

/*
 * A slim white band under the hero card where the Sealion repeatedly drives
 * across. Same `multiply` blend trick as the splash, so only the car is
 * visible against the app's white.
 */
.run-strip {
  position: relative;
  height: 132px;
  margin: -8px 0 18px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  /* Pure white for the same reason as the splash: the car is multiplied
     over it, so a tint here would draw its photo edge. */
  background: var(--paper-pure);
  border: 1px solid var(--line);
}

.run-strip__marks {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: 74px;
  padding-left: 24px;
  opacity: 0.08;
  color: var(--text);
  pointer-events: none;
}

.run-strip__marks .byd-mark {
  width: 44px;
  flex: none;
  opacity: 1;
}

.run-strip__road {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 20px;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    var(--white-20) 22%,
    var(--white-20) 78%,
    transparent
  );
}

.run-strip__car {
  position: absolute;
  bottom: 2px;
  left: 0;
  width: 268px;
  mix-blend-mode: multiply;
  animation: car-run 7s linear infinite;
  will-change: transform;
}

.run-strip__car img {
  display: block;
  width: 100%;
  height: auto;
}

/* Motion lines trailing the car — ink on white. */
.run-strip__car::after {
  content: '';
  position: absolute;
  right: 88%;
  top: 46%;
  width: 220px;
  height: 2px;
  background: linear-gradient(90deg, transparent, color-mix(in oklch, var(--accent) 42%, transparent));
  filter: blur(1px);
}

.run-strip__label {
  position: absolute;
  right: 16px;
  top: 14px;
  font-size: 10px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--text-faint);
}

@keyframes car-run {
  0% {
    transform: translateX(-280px);
  }
  100% {
    transform: translateX(calc(100vw + 60px));
  }
}

/* ------------------------------------------------------------------ */
/* Custom launch (カスタム起動) builder                                 */
/* ------------------------------------------------------------------ */

.custom-macros {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 12px;
  margin-bottom: 14px;
}

/* A saved custom macro. White border marks it as the user's own creation. */
.custom-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 14px 12px;
  border-radius: var(--radius);
  background: linear-gradient(160deg, var(--surface-2), var(--surface));
  border: 1px solid var(--line);
  overflow: hidden;
}

.custom-card__mark {
  position: absolute;
  right: 10px;
  top: 10px;
  width: 34px;
  color: var(--white);
  opacity: 0.16;
  pointer-events: none;
}

.custom-card__name {
  font-weight: 650;
  font-size: 14px;
  padding-right: 40px;
}

.custom-card__steps {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.step-chip {
  font-size: 10.5px;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--white-06);
  border: 1px solid var(--line);
  color: var(--text-dim);
  white-space: nowrap;
}

.step-chip__n {
  color: var(--white);
  opacity: 0.55;
  margin-right: 4px;
}

.custom-card__actions {
  display: flex;
  gap: 8px;
  margin-top: auto;
  padding-top: 4px;
}

.btn--run {
  flex: 1;
  padding: 9px 12px;
  font-size: 13px;
  background: var(--white);
  border-color: var(--white);
  color: var(--on-ink);
}

.btn--run:hover:not(:disabled) {
  background: var(--platinum);
  border-color: var(--platinum);
  box-shadow: 0 0 0 3px var(--white-12);
}

.btn--icon {
  padding: 9px 11px;
  font-size: 13px;
  color: var(--text-dim);
}

.custom-empty {
  padding: 18px;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  color: var(--text-dim);
  font-size: 13px;
  text-align: center;
  margin-bottom: 14px;
}

/* --- Builder ---------------------------------------------------- */

.builder {
  border: 1px solid var(--white-12);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 16px;
}

.builder__head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}

.builder__head .byd-mark {
  width: 34px;
  opacity: 0.7;
}

.builder__title {
  font-weight: 650;
  font-size: 14px;
}

.builder__count {
  margin-left: auto;
  font-size: 11px;
  color: var(--text-faint);
  letter-spacing: 0.08em;
}

.builder__label-field {
  margin-bottom: 14px;
}

.builder__label-field input {
  width: 100%;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
}

.builder__label-field input:focus {
  outline: none;
  border-color: var(--white-20);
  box-shadow: var(--ring);
}

.builder__subhead {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 0 0 8px;
}

/* Ordered list of chosen steps. */
.builder__steps {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}

.builder-step {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--line);
}

.builder-step__n {
  width: 22px;
  height: 22px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--white);
  color: var(--on-ink);
  font-size: 11px;
  font-weight: 700;
}

.builder-step__label {
  font-size: 13px;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.builder-step__tools {
  display: flex;
  gap: 4px;
  flex: none;
}

.mini-btn {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 7px;
  border: 1px solid var(--line);
  background: var(--surface-3);
  color: var(--text-dim);
  cursor: pointer;
  font-size: 11px;
  transition: background 0.15s, color 0.15s;
}

.mini-btn:hover:not(:disabled) {
  background: var(--white-06);
  color: var(--white);
}

.mini-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.mini-btn--danger:hover:not(:disabled) {
  background: var(--danger-soft);
  color: var(--danger);
}

.builder__hint {
  font-size: 12px;
  color: var(--text-faint);
  margin: 0 0 16px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px dashed var(--line);
}

/* The action palette to pick steps from. */
.palette {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-bottom: 16px;
}

.palette__btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 12px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text-dim);
  font-family: inherit;
  font-size: 12.5px;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.palette__btn:hover:not(:disabled) {
  background: var(--white-06);
  border-color: var(--white-20);
  color: var(--white);
}

.palette__btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.palette__btn i {
  font-size: 11px;
  opacity: 0.8;
}

/* Parameter pickers shown when the pending action needs them. */
.param-box {
  border: 1px solid var(--white-20);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  padding: 13px;
  margin-bottom: 16px;
}

.param-box__title {
  font-size: 12px;
  color: var(--text);
  margin-bottom: 11px;
  font-weight: 600;
}

.param-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}

.param-row__label {
  font-size: 11.5px;
  color: var(--text-faint);
  min-width: 66px;
}

.seg {
  display: inline-flex;
  gap: 3px;
  padding: 3px;
  border-radius: 9px;
  background: var(--surface-3);
  border: 1px solid var(--line);
}

.seg button {
  padding: 5px 11px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}

.seg button:hover:not(.is-active) {
  background: var(--white-06);
  color: var(--text);
}

.seg button.is-active {
  background: var(--white);
  color: var(--on-ink);
}

.builder__foot {
  display: flex;
  gap: 9px;
  flex-wrap: wrap;
}

/* ------------------------------------------------------------------ */
/* Responsive                                                          */
/* ------------------------------------------------------------------ */

@media (max-width: 900px) {
  .login-shell {
    grid-template-columns: 1fr;
  }

  .login-hero {
    min-height: 300px;
    padding: 26px;
  }

  /*
   * Stacked layout: at one column there is no side-by-side room, so the car
   * stops being an absolutely-positioned atmosphere layer and becomes a real
   * grid row between the lockup and the headline. Absolute positioning here
   * made the photo land on top of the display type (unreadable at 375px).
   */
  /* Explicit rows so the lockup stays first even though the photo is the
     only element with a declared grid-row. */
  .login-hero__top { grid-row: 1; }
  .login-hero__bottom { grid-row: 3; }

  .login-hero__img {
    position: static;
    grid-row: 2;
    top: auto;
    height: auto;
    max-height: 210px;
    margin: var(--space-xs) 0;
    object-position: center center;
  }

  .login-hero::after {
    top: 74px;
  }

  .login-panel {
    border-left: none;
    border-top: 1px solid var(--line-soft);
    padding: 32px 22px 56px;
  }

  .hero-specs {
    gap: 18px;
  }
}

@media (max-width: 640px) {
  body {
    font-size: 14.5px;
  }

  .topbar {
    padding: 10px 14px;
  }

  .run-strip {
    height: 104px;
    margin: -4px 0 16px;
  }

  .run-strip__car {
    width: 176px;
    animation-duration: 5.2s;
  }

  .run-strip__marks {
    gap: 44px;
  }

  .splash__car {
    width: 86vw;
  }

  /*
   * The hero card is height-constrained on phones; the wordmark badge is
   * redundant there because the topbar lockup is already on screen, and
   * keeping it would push the pills over the stat row.
   */
  .hero-card__badge {
    display: none;
  }

  /* Let the note sit under the title instead of fighting it for width. */
  .section__note {
    margin-left: 0;
    text-align: left;
    flex-basis: 100%;
  }

  .custom-macros {
    grid-template-columns: 1fr;
  }

  .builder {
    padding: 13px;
  }

  .param-row__label {
    min-width: 100%;
  }

  .brand__sub {
    display: none;
  }

  .page {
    padding: 14px 14px 150px;
  }

  .hero-card__img {
    height: 240px;
  }

  .hero-card__body {
    padding: 16px;
  }

  .hero-stat__value {
    font-size: 25px;
  }

  .grid--cols2,
  .grid--auto,
  .grid--macros {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .grid--macros {
    grid-template-columns: 1fr;
  }

  /* Seat rows stack so the level selector keeps its full width. */
  .seat-row {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }

  .level-group {
    width: 100%;
  }

  .level-btn {
    flex: 1;
  }

  .pinbar {
    flex-wrap: wrap;
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
    gap: 9px;
  }

  .pinbar__note {
    display: none;
  }

  .toasts {
    top: auto;
    bottom: 96px;
    left: 14px;
    right: 14px;
    max-width: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  /*
   * The blanket rule above would collapse the drive-past to a single
   * instant frame, which looks broken rather than calm. Instead we cancel
   * the motion outright and show the car parked, centred and still.
   */
  .run-strip__car {
    animation: none !important;
    left: 50%;
    transform: translateX(-50%);
  }

  .run-strip__car::after,
  .splash__road {
    display: none;
  }

  .splash__car {
    animation: none !important;
    transform: none;
    opacity: 1;
  }

  .splash__brand {
    animation: none !important;
    opacity: 1;
  }

  /* Still fade the splash away — just without the drive-in. */
  .splash {
    animation: splash-out 0.3s ease 1.1s forwards !important;
  }
}
