/* N·T DRIVE — shared mobile component layer.
 *
 * This is the layer the five screens are built from. Rules:
 *
 *  - NO hex literals. Every colour, space, radius, duration and easing comes
 *    from vendor/nt-drive.css, which is GENERATED from spec/nt-drayvv.edn by
 *    spec/brand.clj. If a value is not in the spec it does not exist
 *    (BRAND.md §4) and it does not go in here either.
 *  - One card grammar, one button grammar, one type scale.
 *  - A surge score is never rendered without its confidence and never in a
 *    decimal (BRAND.md §7). The Score/Conf/Reason trio below is the only way
 *    to show a score; nothing renders a bare number.
 *  - Stale is worse than absent: StaleNotice is the only sanctioned way to
 *    show old data, and it is always visible while it applies.
 */

/* The DATA font, vendored rather than fetched from a CDN. JetBrains Mono is
 * the font every score, €/h figure and timestamp is set in, and those are the
 * numbers a driver makes a decision on. If a CDN is unreachable in a tunnel —
 * which is exactly when this app is used — the figures must still be tabular.
 * Repurposed from the VOLTEX PWA, which already shipped the same woff2.
 */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url('../fonts/jetbrains-mono-latin-wght-normal.woff2') format('woff2');
}

/* ═══ Screens ══════════════════════════════════════════════════════════════
   The map is the home screen and is always mounted underneath (map.js owns
   #map and never tears it down). Every other screen is a full-bleed overlay
   above it, so returning to the map is instant and keeps the tile cache. */

.nt-screen {
  position: fixed;
  inset: 0;
  z-index: 800;
  display: none;
  flex-direction: column;
  background: var(--nt-navy);
  padding-top: var(--safe-t);
  padding-bottom: var(--safe-b);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.nt-screen.on { display: flex; }

.nt-topbar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--nt-s-12);
  padding: var(--nt-s-16) var(--nt-s-16) var(--nt-s-12);
}
.nt-topbar h1 {
  flex: 1 1 auto;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--nt-text);
}
.nt-back {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--nt-r-md);
  background: var(--nt-surface-2);
  color: var(--nt-text-2);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.nt-back:active { transform: scale(0.94); }

.nt-body {
  flex: 1 1 auto;
  padding: 0 var(--nt-s-16) var(--nt-s-24);
  display: flex;
  flex-direction: column;
  gap: var(--nt-s-16);
}

/* ═══ The loop rail ═══════════════════════════════════════════════════════
   decide → commit → navigate → confirm. The rail is the product's spine: it
   makes the state of a run legible at a glance, which is the whole reason a
   driver trusts an app that commits them to a place. */

.nt-steps {
  display: flex;
  align-items: center;
  gap: var(--nt-s-8);
  padding: 0 var(--nt-s-16) var(--nt-s-16);
}
.nt-step {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--nt-text-3);
}
.nt-step .bar {
  height: 3px;
  border-radius: var(--nt-r-full);
  background: var(--nt-line);
}
.nt-step.done { color: var(--nt-text-2); }
.nt-step.done .bar { background: var(--nt-signal); }
.nt-step.now { color: var(--nt-text); }
.nt-step.now .bar { background: var(--mark, var(--nt-surge)); }

/* ═══ Card ═══════════════════════════════════════════════════════════════
   One card grammar. The map's hero and rail are the same shape, flattened. */

.nt-card {
  background: var(--nt-surface);
  border: 1px solid var(--nt-line);
  border-radius: var(--nt-r-lg);
  padding: var(--nt-s-16);
  box-shadow: var(--nt-el-sm);
}
.nt-card.glass {
  background: var(--nt-surface-glass);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}

/* ═══ The score triple — score, confidence, reason ═══════════════════════
   The contract of BRAND.md §7 in one component. A score cannot be shown
   without the other two; that is the point of not passing a bare number
   around. `mark` is the tier property, set by the caller from the ramp. */

.nt-headline { display: flex; align-items: flex-start; gap: var(--nt-s-16); }

.nt-score {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 92px;
}
.nt-score .n {
  font-family: var(--nt-font-money);
  font-variant-numeric: tabular-nums;
  font-size: 46px;
  font-weight: 700;
  line-height: 0.9;
  color: var(--mark, var(--nt-text));
}
.nt-score .d {
  font-family: var(--nt-font-data);
  font-size: 11px;
  color: var(--nt-text-3);
  margin-top: 4px;
}

.nt-tier {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  border-radius: var(--nt-r-full);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: var(--on-mark, var(--nt-on-surge));
  background: var(--mark, var(--nt-calm));
  /* BRAND.md §4: the calm tier is a dead zone and carries almost no colour
     against the navy ground. A mark that faint needs an edge to stay visible
     — the hairline lives here, in the component, rather than as a brighter
     token that would lie about the tier. */
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--mark, var(--nt-calm)) 55%, transparent);
}

.nt-conf {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--nt-font-data);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--nt-text-3);
}
.nt-conf .dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--nt-signal);
}
.nt-conf.low .dot { background: var(--nt-earnings); }
.nt-conf.none .dot { background: var(--nt-text-3); }

.nt-reason {
  margin-top: var(--nt-s-8);
  font-size: 13px;
  line-height: 1.45;
  color: var(--nt-text-2);
}
.nt-reason::before { content: '▸ '; color: var(--mark, var(--nt-text-3)); }

/* ═══ Stats ═════════════════════════════════════════════════════════════
   Every figure is mono with tabular figures, so columns of numbers line up
   and do not jitter as they refresh every 60 s (BRAND.md §5). */

.nt-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--nt-s-1, 1px);
  background: var(--nt-line);
  border: 1px solid var(--nt-line);
  border-radius: var(--nt-r-md);
  overflow: hidden;
}
.nt-stat {
  background: var(--nt-surface);
  padding: var(--nt-s-12) var(--nt-s-8);
  text-align: center;
}
.nt-stat .v {
  font-family: var(--nt-font-money);
  font-variant-numeric: tabular-nums;
  font-size: 19px;
  font-weight: 700;
  color: var(--nt-text);
}
.nt-stat .v.earn { color: var(--nt-earnings); }
.nt-stat .k {
  margin-top: 3px;
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--nt-text-3);
}

/* ═══ Buttons ════════════════════════════════════════════════════════════ */

.nt-actions { display: flex; flex-direction: column; gap: var(--nt-s-8); }

.nt-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--nt-s-8);
  min-height: 52px;            /* thumb, not a mouse target */
  padding: 0 var(--nt-s-16);
  border: 0;
  border-radius: var(--nt-r-md);
  font-family: var(--nt-font-ui);
  font-size: 15px;
  font-weight: 600;
  color: var(--nt-text);
  background: var(--nt-surface-2);
  cursor: pointer;
  transition: transform var(--nt-d-fast) var(--nt-ease-out),
              opacity var(--nt-d-fast) linear;
}
.nt-btn:active { transform: scale(0.985); }
.nt-btn[disabled] { opacity: 0.4; cursor: default; }
.nt-btn.primary {
  background: var(--mark, var(--nt-surge));
  color: var(--on-mark, var(--nt-on-surge));
}
.nt-btn.go { background: var(--nt-signal); color: var(--nt-on-surge); }
.nt-btn.ghost {
  background: transparent;
  border: 1px solid var(--nt-line);
  color: var(--nt-text-2);
}
.nt-btn.danger { background: transparent; border: 1px solid var(--nt-danger); color: var(--nt-danger); }

/* ═══ Stale / absent ════════════════════════════════════════════════════
   BRAND.md §7. Stale is worse than absent, so this never disappears while
   the data is old, and it never sits behind a dismiss. */

.nt-stale {
  display: flex;
  gap: var(--nt-s-12);
  padding: var(--nt-s-12) var(--nt-s-16);
  border-radius: var(--nt-r-md);
  background: color-mix(in srgb, var(--nt-earnings) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--nt-earnings) 34%, transparent);
  font-size: 12px;
  line-height: 1.4;
  color: var(--nt-earnings);
}
.nt-stale.gone {
  background: color-mix(in srgb, var(--nt-danger) 12%, transparent);
  border-color: color-mix(in srgb, var(--nt-danger) 34%, transparent);
  color: var(--nt-danger);
}
.nt-empty {
  padding: var(--nt-s-24) var(--nt-s-16);
  text-align: center;
  font-size: 13px;
  line-height: 1.5;
  color: var(--nt-text-3);
}

/* ═══ Journal ═══════════════════════════════════════════════════════════ */

.nt-run {
  display: flex;
  align-items: center;
  gap: var(--nt-s-12);
  padding: var(--nt-s-12) 0;
  border-bottom: 1px solid var(--nt-line);
}
.nt-run:last-child { border-bottom: 0; }
.nt-run .s {
  flex: 0 0 auto;
  width: 40px;
  font-family: var(--nt-font-money);
  font-variant-numeric: tabular-nums;
  font-size: 17px;
  font-weight: 700;
  text-align: center;
  color: var(--mark, var(--nt-text-2));
}
.nt-run .m { flex: 1 1 auto; min-width: 0; }
.nt-run .z {
  font-size: 14px;
  font-weight: 600;
  color: var(--nt-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nt-run .t {
  font-family: var(--nt-font-data);
  font-size: 10px;
  color: var(--nt-text-3);
}
.nt-run .o {
  flex: 0 0 auto;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--nt-signal);
}
.nt-run .o.miss { color: var(--nt-text-3); }
.nt-run.live .o { color: var(--nt-earnings); }

/* ═══ Breakdown (why the score is what it is) ═══════════════════════════
   The multipliers VOLTEX actually applied, biggest first. A driver who
   wonders why 79 and not 82 gets an answer here instead of a shrug. */

.nt-kv { display: flex; flex-direction: column; gap: 1px; background: var(--nt-line); border-radius: var(--nt-r-sm); overflow: hidden; }
.nt-kv > div {
  display: flex;
  justify-content: space-between;
  gap: var(--nt-s-12);
  padding: var(--nt-s-8) var(--nt-s-12);
  background: var(--nt-surface);
  font-size: 12px;
  color: var(--nt-text-2);
}
.nt-kv .v { font-family: var(--nt-font-data); font-variant-numeric: tabular-nums; color: var(--nt-text); }

@media (prefers-reduced-motion: reduce) {
  .nt-btn { transition: none; }
  .nt-btn:active { transform: none; }
}

/* ═══ Bottom tab bar ═══════════════════════════════════════════════════════
   CARTE · ZONES · GAINS · ALERTES · PROFIL — five destinations, always in the
   thumb zone, which is the only part of a phone a driver can hit without
   looking. The active tab carries the earnings accent; the rest recede. */
.nt-tabs {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 900;
  display: flex;
  background: var(--nt-surface);
  border-top: 1px solid var(--nt-line);
  padding-bottom: var(--safe-b);
  box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.45);
}
.nt-tab {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  min-height: 56px;
  padding: 8px 0 6px;
  border: 0;
  background: none;
  color: var(--nt-text-3);
  font-family: var(--nt-font-ui);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}
.nt-tab .ico { font-size: 17px; line-height: 1; }
.nt-tab.on { color: var(--nt-earnings); }
.nt-tab:active { opacity: 0.6; }

/* A tabbed screen reserves room for the bar; a pushed detail screen (zone,
   nav, confirm) does not — it is a level deeper and owns the full height. */
.nt-screen.tabbed { padding-bottom: calc(56px + var(--safe-b)); }

/* ═══ Score colour treatment ═══════════════════════════════════════════════
   BRAND.md §7 says a score takes the tier of the §4 ramp, "the same tier as the
   map, the sheet and the push". The f-set reference screens render every score
   in one flat amber instead — a real conflict between the canon and the design
   being matched. Rather than silently pick one, both are built and the reader
   chooses: [data-score-treatment="ramp"] colours by tier, ["flat"] is uniform. */
[data-score-treatment="flat"] .nt-score .n,
[data-score-treatment="flat"] .nt-run .s,
[data-score-treatment="flat"] .nt-alert .s { color: var(--nt-earnings); }

/* ═══ Splash / onboarding ══════════════════════════════════════════════════
   Shown once on first launch, reopenable from the profile. Copy is the f-set
   French, because BRAND.md §2 makes the driver's language the product's
   language; the archive carries an English variant of the same three beats. */
.nt-splash {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--nt-s-20);
  padding: var(--nt-s-32) var(--nt-s-24);
  background: var(--nt-navy);
}
.nt-splash .brand {
  font-family: var(--nt-font-ui);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-align: center;
  color: var(--nt-text);
}
.nt-splash .brand .v { color: var(--nt-signal); }
.nt-splash .tagline {
  font-size: 13px;
  color: var(--nt-text-2);
  text-align: center;
  margin-top: calc(var(--nt-s-8) * -1);
}
.nt-splash .steps {
  display: flex;
  flex-direction: column;
  gap: var(--nt-s-8);
  width: 100%;
  max-width: 420px;
}
.nt-splash .step {
  border: 1px solid var(--nt-line);
  border-radius: var(--nt-r-sm);
  padding: var(--nt-s-12) var(--nt-s-16);
  background: var(--nt-surface);
  text-align: left;
}
.nt-splash .step b {
  display: block;
  font-size: 14px;
  color: var(--nt-text);
  margin-bottom: 2px;
}
.nt-splash .step span {
  font-size: 12px;
  line-height: 1.45;
  color: var(--nt-text-2);
}
.nt-splash .dots { display: flex; gap: 6px; }
.nt-splash .dots i {
  width: 22px;
  height: 3px;
  border-radius: var(--nt-r-full);
  background: var(--nt-line);
}
.nt-splash .dots i.on { background: var(--nt-earnings); }
/* width:100% on .actions is not enough: .nt-splash is align-items:center, which
   lets the flex child shrink to its content, so the CTA came out narrow and
   left-aligned instead of the full-width primary the f-set shows. */
.nt-splash .actions { width: 100%; max-width: 420px; }
.nt-splash .actions .nt-btn { width: 100%; }
.nt-splash .foot {
  font-size: 10px;
  color: var(--nt-text-3);
  text-align: center;
}

/* ═══ Gains ════════════════════════════════════════════════════════════════
   The screen the f-set calls "ce qui reste dans la poche" — what is actually
   left after the platform, the social charges and the day's fixed costs, and
   which zones are worth the drive. The big figure is NET, not gross; the gross
   is shown beside it precisely so the driver can see the gap. */
.nt-pocket { text-align: center; padding: var(--nt-s-20) var(--nt-s-16); }
.nt-pocket .k {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--nt-text-3);
}
.nt-pocket .n {
  font-family: var(--nt-font-money);
  font-variant-numeric: tabular-nums;
  font-size: 52px;
  font-weight: 700;
  line-height: 1;
  color: var(--nt-earnings);
}
.nt-pocket .n.neg { color: var(--nt-danger); }
.nt-pocket .per { font-size: 18px; color: var(--nt-text-2); }
.nt-pocket .why {
  margin-top: var(--nt-s-8);
  font-size: 12px;
  line-height: 1.45;
  color: var(--nt-text-3);
}

/* The 4/5/6/8h picker. Fixed costs are per DAY, so the length of the shift is
   the single biggest lever the driver has, and the only one the app cannot do
   for them. */
.nt-hours { display: flex; gap: var(--nt-s-8); }
.nt-hours button {
  flex: 1 1 0;
  min-height: 40px;
  border: 1px solid var(--nt-line);
  border-radius: var(--nt-r-sm);
  background: var(--nt-surface);
  color: var(--nt-text-2);
  font-family: var(--nt-font-money);
  font-size: 13px;
  cursor: pointer;
}
.nt-hours button.on {
  background: var(--nt-earnings);
  border-color: var(--nt-earnings);
  color: var(--nt-on-surge);
  font-weight: 700;
}

/* Ranked by valeur réelle. The net figure leads and the gross sits behind it,
   because the gap between the two IS the product's value proposition. */
.nt-value {
  border: 1px solid var(--nt-line);
  border-radius: var(--nt-r-sm);
  padding: var(--nt-s-12) var(--nt-s-16);
  margin-bottom: var(--nt-s-8);
  background: var(--nt-surface);
}
.nt-value .top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--nt-s-8);
}
.nt-value .z { font-size: 15px; font-weight: 700; color: var(--nt-text); }
.nt-value .net {
  font-family: var(--nt-font-money);
  font-variant-numeric: tabular-nums;
  font-size: 19px;
  font-weight: 700;
  color: var(--nt-earnings);
}
.nt-value .net.neg { color: var(--nt-danger); }
.nt-value .sub {
  margin-top: 3px;
  display: flex;
  gap: var(--nt-s-8);
  align-items: center;
  font-family: var(--nt-font-data);
  font-size: 10px;
  color: var(--nt-text-3);
}
.nt-verdict {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.nt-verdict.perte { color: var(--nt-danger); }
.nt-verdict.difficile { color: var(--nt-earnings); }
.nt-verdict.ok { color: var(--nt-signal); }

.nt-section {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--nt-earnings);
  margin: var(--nt-s-8) 0 var(--nt-s-4);
}

/* Costs are the driver's own numbers, so editing them is a first-class action,
   not a buried settings screen. */
.nt-costs { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--nt-s-8); }
.nt-costs label { display: block; font-size: 10px; color: var(--nt-text-3); }
.nt-costs input {
  width: 100%;
  margin-top: 4px;
  min-height: 44px;
  padding: 0 var(--nt-s-8);
  border: 1px solid var(--nt-line);
  border-radius: var(--nt-r-sm);
  background: var(--nt-surface-2);
  color: var(--nt-text);
  font-family: var(--nt-font-money);
  font-size: 15px;
