/*==============================================================================
  app.css — this product's layer on top of the Industry design system.

  RULES
  -----
  • assets/ds/styles.css is the source of truth and is never edited here — it is
    regenerated from claude.ai/design. Everything product-specific lives in this
    file and overrides by cascade.
  • Colours, spacing, radii and shadows come from tokens (var(--color-*),
    var(--space-*), …). No raw hex, no invented scale.
  • MOBILE FIRST. Base rules are the phone. Anything wider is a min-width
    addition. Most visitors arrive on a phone; the desktop is the special case.

  ⚠️ TOUCH TARGETS. Industry sizes .btn at ~34px and .input at 36px, which is
  right for a dense desktop tool and below the 44px minimum on a phone. They are
  raised here rather than in the system file.
==============================================================================*/

:root {
  --map-h-mobile: 52dvh;   /* dvh, not vh: the mobile address bar collapses */
  --bar-h: 64px;
  --z-suggest: 900;        /* above the sticky bar … */
  --z-bar: 800;
  --z-overlay: 10000;      /* … below the popover layer */
  --z-popover: 10050;
  --z-sheet: 10060;        /* funnel sits above popovers: it gates them */
  --z-toast: 10100;
}

html, body { height: 100%; }
body { overflow-x: hidden; }   /* belt and braces; the gate test asserts none is needed */

/* ⚠️ The `hidden` attribute must always win.
   The UA stylesheet gives [hidden] a display:none of the lowest specificity, so
   ANY author rule that sets display — .btn{display:inline-flex} from the design
   system, .route-summary{display:flex} here — silently defeats it. That is how
   an empty "Distance / Driving time" frame appeared before anything was
   calculated, and how a Share button marked hidden was on screen anyway.
   Half the app's state is expressed with this attribute, so it gets one rule
   rather than a growing list of per-component patches. */
[hidden] { display: none !important; }

/*==============================================================================
  Touch targets
==============================================================================*/

.btn { min-height: 44px; padding-inline: var(--space-4); }
.btn-ghost { min-height: 44px; }
.btn-icon { width: 44px; height: 44px; }
.input { min-height: 44px; font-size: 16px; }
/* ⚠️ 16px is not a style choice: iOS Safari zooms the whole page when a focused
   input is smaller, and the user then has to pinch back out. */

@media (min-width: 768px) {
  .input { font-size: 14px; }   /* no auto-zoom on desktop, so density returns */
}

/*==============================================================================
  Shell
==============================================================================*/

.nav {
  border-bottom: 1px solid var(--color-divider);
  gap: var(--space-3);
}
.nav-brand { font-size: 17px; letter-spacing: 0.01em; }
.nav-brand-lite { color: var(--color-accent); }
.nav a {
  min-height: 44px; display: inline-flex; align-items: center;
  padding-inline: var(--space-2);
}

.page {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-4);
  padding-bottom: calc(var(--space-8) + env(safe-area-inset-bottom));
  max-width: 1280px;
  margin: 0 auto;
}

.planner-title { font-size: 28px; margin-bottom: var(--space-2); }
.planner-lede { font-size: 14px; margin-bottom: var(--space-4); }

.footer {
  display: flex; flex-direction: column; gap: var(--space-1);
  padding: var(--space-6) var(--space-4) calc(var(--space-8) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--color-divider);
  font-size: 12px;
}

/* Desktop: form beside the map. On a phone they stack, form first — the map is
   useless until there is something to show on it. */
@media (min-width: 1024px) {
  .page { flex-direction: row; align-items: flex-start; gap: var(--space-6); }
  .planner { flex: 0 0 420px; }
  .map-section { flex: 1; position: sticky; top: var(--space-4); }
  .planner-title { font-size: 34px; }
}

/*==============================================================================
  Fields
==============================================================================*/

.field { margin-bottom: var(--space-4); }
.field > label { font-size: 13px; }
.input-holder { position: relative; }

.address-wrapper .btn-block { margin-top: var(--space-2); }

.hint { font-size: 12px; margin: var(--space-1) 0 0; color: color-mix(in srgb, var(--color-text) 60%, transparent); }
.hint-warn { color: var(--color-accent-800); }
.hint.is-active { color: var(--color-accent-700); }

/*==============================================================================
  Suggestions
==============================================================================*/

/* ⚠️ Visibility is driven by the data-is-visible ATTRIBUTE, which the search
   widget toggles — not by the `hidden` property. Putting `hidden` on the list in
   the markup keeps it invisible forever, because nothing ever removes it. */
.address-suggestions { display: none; }
.address-suggestions[data-is-visible] { display: block; }

.address-suggestions {
  position: absolute; left: 0; right: 0; top: 100%;
  margin: 2px 0 0; padding: 0; list-style: none;
  background: var(--color-bg);
  border: 1px solid var(--color-divider);
  box-shadow: var(--shadow-md);
  /* ⚠️ Must clear the sticky action bar (--z-bar). On mobile the Finish field's
     dropdown overlaps it, and a suggestion you cannot tap is worse than none. */
  z-index: var(--z-suggest);
  /* 44vh with its own scroll: a full-height list disappears under the on-screen
     keyboard, which on a phone covers roughly half the viewport. */
  max-height: 44vh; overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.address-suggestions ul { margin: 0; padding: 0; list-style: none; }
.address-suggestions li {
  padding: var(--space-3);
  min-height: 44px; display: flex; align-items: center;
  font-size: 14px; cursor: pointer;
  border-bottom: 1px solid color-mix(in srgb, var(--color-text) 8%, transparent);
}
.address-suggestions li:last-child { border-bottom: 0; }
.address-suggestions li:hover,
.address-suggestions li.is-selected { background: var(--color-accent-100); }

/*==============================================================================
  Stop list
==============================================================================*/

.address-list { list-style: none; margin: 0 0 var(--space-4); padding: 0; }

#address-list > li.address {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) 0 var(--space-2) var(--space-3);
  border: 1px solid var(--color-divider);
  border-bottom: 0;
  font-size: 14px; line-height: 1.35;
  background: var(--color-bg);
  cursor: grab;
  touch-action: pan-y;   /* vertical scroll still belongs to the page */
}
#address-list > li.address:last-child { border-bottom: 1px solid var(--color-divider); }
#address-list > li.address > span:first-child { color: var(--color-accent-700); flex: none; }
#address-list > li.address .spacer-r { flex: 1; }

/* Delete: the picture stays 16px, the tappable area is 44×44. Negative margin
   keeps the row from growing to fit it. */
#address-list > li.address > img {
  width: 16px; height: 16px; box-sizing: content-box;
  padding: 14px; margin: -14px;
  flex: none; cursor: pointer; opacity: .55;
}
#address-list > li.address > img:hover { opacity: 1; }

#address-list > li.address.is-selected {
  background: var(--color-accent-100);
  border-color: var(--color-accent);
}
#address-list > li.address.dragged { opacity: .5; }
#address-list > li.placeholder {
  height: 44px; border: 1px dashed var(--color-accent);
  background: var(--color-accent-100);
}
/* A stop that looks nowhere near the rest. Amber-ish accent step, never red:
   this is a warning, not a verdict — the user may really be driving there. */
#address-list > li.address.is-far { border-left: 3px solid var(--color-accent-600); }
#address-list > li.address.is-far::before { content: "⚠"; margin-right: var(--space-1); }

.points-counter { font-size: 12px; color: color-mix(in srgb, var(--color-text) 60%, transparent); }

/*==============================================================================
  Action bar — sticky on phones
==============================================================================*/

.address-list-buttons-wrapper {
  display: flex; align-items: center; gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-3) 0;
}
.action-buttons { display: flex; gap: var(--space-2); margin-left: auto; }
.action-buttons .btn { white-space: nowrap; }

@media (max-width: 767px) {
  .address-list-buttons-wrapper {
    position: sticky; bottom: 0; z-index: var(--z-bar);
    background: var(--color-bg);
    border-top: 1px solid var(--color-divider);
    padding: var(--space-2) var(--space-3) calc(var(--space-2) + env(safe-area-inset-bottom));
    margin-inline: calc(var(--space-4) * -1);
  }
  .action-buttons { flex: 1; margin-left: 0; }
  .action-buttons .btn { flex: 1; }
  .points-counter { flex-basis: 100%; }
}

/*==============================================================================
  Result
==============================================================================*/

.route-summary {
  display: flex; flex-wrap: wrap; gap: var(--space-4);
  padding: var(--space-3); margin-bottom: var(--space-3);
}
.route-summary-item { display: flex; flex-direction: column; }
.route-summary-label {
  font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--color-accent);
}
.route-summary-value {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 22px;
}
.route-summary-note { flex-basis: 100%; font-size: 11px; }

.route-legs { margin-bottom: var(--space-4); }
.route-legs-list { margin: var(--space-2) 0 0; padding-left: var(--space-6); font-size: 13px; }
.route-legs-list li { margin-bottom: var(--space-1); }

/*==============================================================================
  Map
==============================================================================*/

.map {
  height: var(--map-h-mobile);
  min-height: 260px;
  border: 1px solid var(--color-divider);
  background: var(--color-surface);
  display: flex; align-items: center; justify-content: center;
  color: color-mix(in srgb, var(--color-text) 45%, transparent);
  font-size: 13px;
}
.leaflet-container { background: var(--color-surface); font-family: var(--font-body); }

@media (min-width: 1024px) {
  .map { height: calc(100dvh - 120px); }
}

/* Fullscreen map: the control sets this class on <html>. */
html.map-is-fullscreen .map {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  height: 100dvh; border: 0;
}
html.map-is-fullscreen body { overflow: hidden; }

/* Leaflet ships its own chrome; bring it onto the system's tokens.
   ⚠️ `.leaflet-touch .leaflet-bar a` must be in the selector: Leaflet's own
   touch rules are more specific than a bare `.leaflet-bar a` and pin the
   controls at 30px, which is under the 44px minimum. The plain selector looked
   right and did nothing — the audit caught it at 30x30. */
.leaflet-bar a, .leaflet-bar a:hover,
.leaflet-touch .leaflet-bar a, .leaflet-touch .leaflet-bar a:hover,
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out,
.map-btn-control, .leaflet-touch .map-btn-control {
  width: 44px; height: 44px; line-height: 44px;
  background: var(--color-bg); color: var(--color-text);
  border-bottom-color: var(--color-divider);
  font-family: var(--font-heading);
  font-size: 18px; text-align: center; text-decoration: none;
}
.leaflet-bar a:hover { background: var(--color-accent-100); }
.leaflet-bar { border: 1px solid var(--color-divider); border-radius: 0; box-shadow: var(--shadow-sm); }
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
  background: var(--color-bg); color: var(--color-text);
  border-radius: 0; box-shadow: var(--shadow-md);
}
.leaflet-control-attribution {
  background: color-mix(in srgb, var(--color-bg) 85%, transparent);
  font-size: 10px;
}

/* One finger scrolls the page, two move the map. The hint appears on a
   one-finger drag. (Set up by the map module on touch screens only.) */
/* ⚠️ Hidden by default. The module shows it by adding .is-visible on a
   one-finger drag and removes it after 1.4 s; without this rule the overlay sat
   on the map permanently, covering it with an instruction nobody asked for. */
.map-gesture-hint { opacity: 0; transition: opacity .15s; }
.map-gesture-hint.is-visible { opacity: 1; }

.map-gesture-hint {
  position: absolute; inset: 0; z-index: 500;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--color-neutral-900) 45%, transparent);
  color: var(--color-bg); font-size: 14px; text-align: center;
  padding: var(--space-6); pointer-events: none;
}

/*==============================================================================
  Markers — 0×0 anchor, bubble lifted by CSS
==============================================================================*/
/* ⚠️ .mrk-badge must be a 0×0 box exactly at the coordinate. mini had a bug
   where the bubble sat ~22px above the real point because the wrapper had both
   bottom:0 and translateY(-100%). Do not give this element size or padding. */
.mrk-badge { position: relative; display: block; width: 0; height: 0; }
.mrk-bubble {
  position: absolute; bottom: 6px; left: 50%; transform: translateX(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 24px; height: 24px; padding: 0 7px;
  background: var(--color-accent); color: var(--color-bg);
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 13px; line-height: 1; white-space: nowrap;
  border: 1px solid var(--color-accent-700);
}
/* The tail is a real 6px triangle. mini used a rotated square whose tip landed
   on a fractional pixel. */
.mrk-tail {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 6px solid var(--color-accent);
}
.mrk-start .mrk-bubble { background: var(--color-accent-900); border-color: var(--color-accent-900); }
.mrk-start .mrk-tail { border-top-color: var(--color-accent-900); }
.mrk-finish .mrk-bubble { background: var(--color-accent-700); border-color: var(--color-accent-700); }
.mrk-finish .mrk-tail { border-top-color: var(--color-accent-700); }
.mrk-active .mrk-bubble { outline: 2px solid var(--color-text); outline-offset: 1px; }
.mrk-far .mrk-bubble { background: var(--color-neutral-700); border-color: var(--color-neutral-900); }
.mrk-far .mrk-tail { border-top-color: var(--color-neutral-700); }

/*==============================================================================
  Overlay, popovers, notifications
==============================================================================*/

#overlay {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  background: color-mix(in srgb, var(--color-neutral-900) 55%, transparent);
}
#overlay[hidden] { display: none; }

.popover {
  position: fixed; z-index: var(--z-popover);
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: min(40rem, calc(100vw - 2rem));
  max-height: calc(100dvh - 2rem);
  display: flex; flex-direction: column;
  padding: var(--space-6);
  background: var(--color-bg);
  box-shadow: var(--shadow-lg);
}
/* ⚠️ Required: display:flex above would otherwise defeat the hidden attribute. */
.popover[hidden] { display: none; }
.popover h2 { margin-top: 0; }

/* ⚠️ .close-icon carries margin-left:auto in some kits, and an auto margin on
   the cross axis of a flex item cancels stretch — which once rendered a "Done"
   button 38px wide with the word cut in half. Pin the geometry here so the next
   such button does not inherit the bug. */
.popover button.close-icon {
  align-self: flex-end; margin-left: 0; width: auto;
  flex: 0 0 auto; white-space: nowrap;
}
.popover > button.close-icon {
  position: absolute; top: var(--space-2); right: var(--space-2);
  width: 44px; height: 44px; padding: 0;
  font-size: 22px; line-height: 1;
  background: transparent; border: 0; cursor: pointer; color: var(--color-text);
}

.bulk-import-content { min-height: 0; overflow-y: auto; flex: 1; }
.bulk-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-3); }
.bulk-actions-footer { border-top: 1px solid var(--color-divider); padding-top: var(--space-3); }
.bulk-progress-track { height: 4px; background: var(--color-neutral-300); }
.bulk-progress-bar { height: 100%; width: 0; background: var(--color-accent); transition: width .2s; }
.clarify-list { display: flex; flex-direction: column; gap: var(--space-3); }
.clarify-list .radio { min-height: 44px; }
.fail-list { font-size: 13px; padding-left: var(--space-6); }
.share-link { word-break: break-all; font-size: 13px; }

#notificaitons-wrapper {
  position: fixed; z-index: var(--z-toast);
  top: var(--space-3); left: var(--space-3); right: var(--space-3);
  display: flex; flex-direction: column; gap: var(--space-2);
  pointer-events: none;
}
.error-message {
  pointer-events: auto; position: relative;
  padding: var(--space-3) 44px var(--space-3) var(--space-3);
  background: var(--color-bg);
  border: 1px solid var(--color-accent-700);
  box-shadow: var(--shadow-md);
}
.error-message[hidden] { display: none; }
.notification-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: 15px;
}
.notification-text { font-size: 13px; }
.error-message .close-icon {
  position: absolute; top: 0; right: 0; width: 44px; height: 44px;
  background: transparent; border: 0; font-size: 20px; cursor: pointer; color: var(--color-text);
}

@media (min-width: 768px) {
  #notificaitons-wrapper { left: auto; width: 380px; }
}

/* Splash while the router works */
.splash { width: min(22rem, calc(100vw - 2rem)); text-align: center; }
.splash-track { height: 3px; background: var(--color-neutral-300); overflow: hidden; }
.splash-bar { height: 100%; width: 40%; background: var(--color-accent); animation: splash-slide 1.1s infinite ease-in-out; }
@keyframes splash-slide { 0% { transform: translateX(-100%); } 100% { transform: translateX(350%); } }
@media (prefers-reduced-motion: reduce) {
  .splash-bar { animation: none; width: 100%; }
}

.undo-toast {
  position: fixed; z-index: var(--z-toast);
  left: var(--space-3); right: var(--space-3);
  bottom: calc(var(--space-3) + env(safe-area-inset-bottom));
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--color-neutral-900); color: var(--color-neutral-100);
  font-size: 13px;
}
.undo-toast[hidden] { display: none; }
.undo-toast .btn { color: var(--color-accent-300); margin-left: auto; }

/*==============================================================================
  Funnel — bottom sheet on a phone, centred card on a desktop
==============================================================================*/
/* A bottom sheet is the right mobile shape: it arrives from the thumb end of
   the screen, and the primary action lands where the thumb already is. */

.sheet-backdrop {
  position: fixed; inset: 0; z-index: var(--z-sheet);
  display: flex; align-items: flex-end; justify-content: center;
  background: color-mix(in srgb, var(--color-neutral-900) 55%, transparent);
}
.sheet-backdrop[hidden] { display: none; }

.sheet {
  position: relative;
  width: 100%; max-height: calc(100dvh - var(--space-8));
  overflow-y: auto;
  padding: var(--space-6) var(--space-4) calc(var(--space-6) + env(safe-area-inset-bottom));
  background: var(--color-bg);
  box-shadow: var(--shadow-lg);
  animation: sheet-rise .22s ease-out;
}
@keyframes sheet-rise { from { transform: translateY(12%); opacity: .6; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .sheet { animation: none; } }

.sheet > .close-icon {
  position: absolute; top: var(--space-2); right: var(--space-2);
  width: 44px; height: 44px; padding: 0;
  background: transparent; border: 0; font-size: 22px; cursor: pointer; color: var(--color-text);
}
.sheet-title { font-size: 24px; margin-bottom: var(--space-2); }
.sheet-lede { font-size: 15px; }
.sheet-fineprint { font-size: 12px; text-align: center; margin: var(--space-2) 0 0; }

@media (min-width: 768px) {
  .sheet-backdrop { align-items: center; }
  .sheet {
    width: min(30rem, calc(100vw - 2rem));
    max-height: calc(100dvh - 4rem);
    padding: var(--space-8);
    animation: none;
  }
}

/* Plans */
.plans { display: flex; flex-direction: column; gap: var(--space-3); margin: var(--space-4) 0; }

.plan {
  position: relative; display: block; cursor: pointer;
  padding: var(--space-4);
  border: 1px solid var(--color-divider);
  min-height: 44px;
}
.plan input { position: absolute; opacity: 0; width: 0; height: 0; }
.plan:hover { background: color-mix(in srgb, var(--color-text) 4%, transparent); }
.plan:has(input:checked) {
  border-color: var(--color-accent);
  box-shadow: inset 0 0 0 1px var(--color-accent);
  background: var(--color-accent-100);
}
.plan:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: 2px; }

.plan-head { display: flex; align-items: baseline; gap: var(--space-2); }
.plan-name {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: 18px;
}
.plan-price {
  margin-left: auto;
  font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: 22px;
}
.plan-sub { font-size: 12px; color: color-mix(in srgb, var(--color-text) 60%, transparent); }
.plan-badge {
  position: absolute; top: calc(var(--space-3) * -1); left: var(--space-3);
  background: var(--color-accent); color: var(--color-bg);
  font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  padding: 2px 8px;
}

#testmode-email-error { margin-top: var(--space-1); }
