/* RouteAll console — shared styles.
 *
 * Tokens are copied VERBATIM from support.html, which copied them from
 * index.html, so the console reads as the same product. Do not "improve" a
 * value here in isolation: three files share this palette and a console that
 * is a slightly different blue looks like a phishing page, not a new section.
 */
:root{--bg:#0A0E14;--line:#232D3A;--text:#EDF2F7;--dim:#828FA6;
      --accent:#4DABF7;--green:#10B981;--r:14px;--max:760px;--focus:#8FD3FF}

/* Console-only additions, named in the same style. `--card` is the fill
   support.html hardcodes as #0E141C on .card; it is a token here because the
   console has many more surfaces than one contact box. */
:root{--card:#0E141C;--body:#C7D2E0;--red:#F85149;--max-narrow:460px}
/* Surfaces from the console redesign proposal — the only additions it makes.
   `raised` lifts the header and the Account hero off the page background,
   `raised-2` is the filled active pill, `sunk` is an inset well, `line-soft`
   a rule inside a card. Same hue family as everything above. */
:root{--raised:#121A25;--raised-2:#1A2434;--sunk:#070B10;--line-soft:#1A2330}

*{box-sizing:border-box}
/* The `hidden` ATTRIBUTE must win over any display rule below.
   The UA sheet sets [hidden]{display:none} at a lower specificity than a class
   rule, so `.link-row{display:flex}` on a hidden element re-showed it — the
   forgot-password row rendered in sign-up mode with hidden=true set. Hidden is
   also what keeps those controls out of the tab order, so this is an a11y bug,
   not a cosmetic one. Anything hidden here is hidden, full stop. */
[hidden]{display:none!important}
body{margin:0;background:var(--bg);color:var(--text);
     font:16px/1.7 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
     -webkit-font-smoothing:antialiased}
a{color:var(--accent)}
a:focus-visible,button:focus-visible,summary:focus-visible,
input:focus-visible,[role=radio]:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.wrap{max-width:var(--max);margin:0 auto;padding:0 20px}

/* ── Header / footer — markup and rules from support.html ─────────────────── */
header{border-bottom:1px solid var(--line);position:sticky;top:0;
       background:rgba(10,14,20,.9);backdrop-filter:blur(8px);z-index:10}
header .wrap{display:flex;align-items:center;justify-content:space-between;height:64px}
.brand{display:flex;align-items:center;gap:10px;font-weight:700;
       color:var(--text);text-decoration:none;font-size:17px;min-height:44px}
.brand img{border-radius:7px}
.back{color:var(--dim);text-decoration:none;font-size:14px}
.back:hover{color:var(--text)}
main{padding:56px 0 72px}
h1{font-size:clamp(28px,5vw,40px);line-height:1.15;letter-spacing:-.02em;margin:0 0 12px}
.lede{font-size:17px;color:var(--body);margin:0 0 32px}

footer{border-top:1px solid var(--line);padding:28px 0;color:var(--dim);font-size:14px}
footer .wrap{display:flex;flex-wrap:wrap;gap:14px;justify-content:space-between}
footer a{color:var(--dim);text-decoration:none;margin-right:16px}
footer a:hover{color:var(--text)}

/* Skip link — verbatim from support.html. A keyboard or switch user landing
   here would otherwise tab the whole sticky header before reaching the form
   (WCAG 2.4.1). */
.skip{position:absolute;left:-9999px;top:0;z-index:100;
      background:var(--accent);color:#06121F;padding:11px 18px;
      border-radius:0 0 10px 0;font-weight:700;text-decoration:none}
.skip:focus{left:0}

/* Touch targets — the same technique support.html uses. Padding raises the hit
   box to 44px; the negative margin absorbs the extra height so the 64px header
   and footer rhythm are visually unchanged. */
.back{display:inline-flex;align-items:center;min-height:44px;margin:-10px 0}
footer a{display:inline-flex;align-items:center;min-height:44px;margin:-13px 16px -13px 0}

/* ── Auth card ────────────────────────────────────────────────────────────── */
.auth-wrap{max-width:var(--max-narrow);margin:0 auto;padding:0 20px}

/* ── Sign-in split: form left, what-you-get right ───────────────────────────
   Scoped to .auth-split so nothing here reaches the other console pages that
   share this stylesheet. The form keeps its own max-width, so widening the
   page does not stretch the fields — the second column takes the extra room.
   Single column under 940px, growth panel BELOW the form: on a phone the job
   is signing in, and the pitch must not push the fields off-screen. */
.auth-split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
            gap:0 clamp(28px,5vw,72px);align-items:center;
            max-width:1180px;margin:0 auto;padding:0 clamp(16px,4vw,40px)}
/*
  START, not end. The form used to be right-aligned in its track, which put it
  74px inside the header logo now that the header shares this container's
  width. Aligning it to the track start makes one vertical edge run through the
  logo, the chip, the headline, the card and the footer links.
*/
.auth-split > .auth-wrap{max-width:440px;margin:0;padding:0;justify-self:start;width:100%}
@media (max-width:940px){
  /*
    Stacked. The form must FILL the container rather than centre inside it:
    with `max-width: var(--max-narrow); margin: 0 auto` an iPad at 834px put
    the card at x=179 while the header logo sat at x=33 — a 146px inset that
    only appears between the single-column breakpoint and the point where the
    container itself is narrower than the form. Measured, not assumed.
  */
  .auth-split{grid-template-columns:1fr;gap:40px 0}
  .auth-split > .auth-wrap{justify-self:stretch;max-width:none;margin:0}
  /* Same reason: the panel spans the column instead of sitting centred in it. */
  .auth-grow{max-width:none;margin:0}
}

.auth-grow{width:100%;max-width:560px}
@media (max-width:940px){
  /*
    This block used to re-apply `max-width: var(--max-narrow); margin: 0 auto`,
    which silently undid the stretch set in the stacked-layout rule above — it
    comes later in the file, so it won. The panel sat centred while the header
    spanned the full container, and on an iPad the two disagreed by 145px.
    Only the border and padding belong here.
  */
  .auth-grow{max-width:none;border-top:1px solid var(--line);padding-top:32px}
}
.grow-fig{margin:0 0 24px}
.grow-fig svg{width:100%;height:auto;display:block}
.grow-eyebrow{margin:0 0 6px;font-size:12px;font-weight:600;letter-spacing:.1em;
              text-transform:uppercase;color:var(--amber,#F5B942)}
.grow-h{margin:0 0 10px;font-size:clamp(26px,3.4vw,34px);line-height:1.1;
        letter-spacing:-.02em;text-wrap:balance}
.grow-lede{margin:0 0 24px;color:var(--body);font-size:16px;line-height:1.6;max-width:52ch}
.grow-facts{list-style:none;margin:0;padding:0;
            display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
@media (max-width:560px){.grow-facts{grid-template-columns:1fr}}
.grow-facts li{background:var(--card);border:1px solid var(--line);
               border-radius:var(--r);padding:14px}
.grow-facts b{display:block;font-size:30px;line-height:1;font-weight:800;
              color:var(--text);font-variant-numeric:tabular-nums}
.grow-facts b.go{color:var(--green,#10B981)}
.grow-facts span{display:block;margin-top:5px;font-size:13px;color:var(--dim)}

/* Referral chip. Only rendered when ?ref= carried a well-formed code, so its
   presence is itself the signal that attribution is pending. */
.ref-chip{display:flex;align-items:center;gap:10px;margin:0 0 18px;
          padding:11px 13px;border-radius:var(--r);font-size:13.5px;color:var(--body);
          background:rgba(16,185,129,.09);border:1px solid rgba(16,185,129,.3)}
.ref-chip svg{width:17px;height:17px;flex:none;fill:none;
              stroke:var(--green,#10B981);stroke-width:2}
.ref-chip b{color:var(--green,#10B981);font-family:ui-monospace,Menlo,monospace;
            font-weight:500;letter-spacing:.05em}
.card{border:1px solid var(--line);border-radius:var(--r);padding:24px;
      background:var(--card);margin:0 0 20px}

/* Sign in / Sign up toggle. Radios rather than buttons: it is a choice between
   two mutually exclusive states, which is what a radio group means to a screen
   reader. The inputs are visually hidden but still focusable and still the
   thing that carries checked state — never `display:none`, which removes them
   from the tab order and the a11y tree entirely. */
.toggle{display:flex;gap:4px;padding:4px;margin:0 0 24px;
        border:1px solid var(--line);border-radius:12px;background:var(--card)}
.toggle input{position:absolute;opacity:0;width:0;height:0}
.toggle label{flex:1;display:flex;align-items:center;justify-content:center;
              min-height:44px;border-radius:9px;cursor:pointer;
              font-size:15px;font-weight:600;color:var(--dim);
              transition:background .15s,color .15s}
.toggle input:checked + label{background:var(--accent);color:#06121F}
.toggle input:focus-visible + label{outline:2px solid var(--focus);outline-offset:2px}

/* Field label — the app's uppercase micro eyebrow (RegisterScreen fieldLabel). */
.field-label{display:block;font-size:12px;font-weight:700;letter-spacing:.5px;
             text-transform:uppercase;color:var(--dim);margin:0 0 6px}

/* Input shell. 44px floor, radius 10, `surface` fill — §5.8 of the app's
   design system, the same numbers the native screens use. */
.input-wrap{display:flex;align-items:center;gap:8px;
            background:var(--bg);border:1px solid var(--line);border-radius:10px;
            min-height:44px;padding-left:14px;margin:0 0 16px}
.input-wrap:focus-within{border-color:var(--accent)}
.input-wrap input{flex:1;min-width:0;background:none;border:0;outline:none;
                  color:var(--text);font-size:16px;padding:11px 14px 11px 0;
                  font-family:inherit;
                  /* Fill the 44px wrapper: the wrapper is a div, not a label, so
                     a tap on its padding did nothing — the input itself measured
                     41px on a phone. */
                  align-self:stretch}
/* 16px is not a style choice: iOS Safari zooms the whole page when a focused
   input is under 16px, which on a phone reflows the form mid-typing. */
.input-wrap input::placeholder{color:var(--dim)}
/* The eye button reaches the field edge, so the wrapper drops its right pad. */
.input-wrap.has-toggle{padding-right:0}
.eye{width:44px;height:44px;flex:none;display:flex;align-items:center;
     justify-content:center;background:none;border:0;cursor:pointer;
     color:var(--dim);padding:0}
.eye:hover{color:var(--text)}
.eye svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.7;
         stroke-linecap:round;stroke-linejoin:round}
.icon{flex:none;width:18px;height:18px;fill:none;stroke:var(--dim);stroke-width:1.7;
      stroke-linecap:round;stroke-linejoin:round}

.hint{color:var(--dim);font-size:14px;margin:-6px 0 16px}

/* Forgot password — right-aligned, 44px tall like the app's. */
.link-row{display:flex;justify-content:flex-end;margin:0 0 4px}
.link-btn{background:none;border:0;cursor:pointer;color:var(--accent);
          /* longhands: `inherit` is invalid inside the `font` shorthand, which
             dropped the whole declaration and left the UA's 13.33px */
          font-family:inherit;font-size:14px;font-weight:600;line-height:1;
          display:inline-flex;align-items:center;min-height:44px;padding:0 4px}
.link-btn:hover{filter:brightness(1.12)}
.link-btn[disabled]{color:var(--dim);cursor:default}

/* ── Account type — two tiles, side by side ───────────────────────────────── */
/* A fieldset/legend is the right SEMANTICS for a labelled radio group, but the
   UA sheet gives it a groove border and a notched legend that belong to no
   other control on this page. Reset to a plain block; the legend keeps its
   .field-label eyebrow styling. */
.account-type{margin:24px 0 0;border:0;padding:0;min-width:0}
.account-type legend{padding:0}
.tiles{display:flex;align-items:stretch;gap:14px}
.tiles input{position:absolute;opacity:0;width:0;height:0}
.tile{flex:1;display:flex;flex-direction:column;align-items:center;
      justify-content:flex-start;text-align:center;cursor:pointer;position:relative;
      min-height:44px;padding:14px 10px;border-radius:16px;
      border:1.5px solid var(--line);background:var(--card)}
/* 1.5px, not the 1px hairline: at 1px the selected tile's blue edge is thin
   enough that in direct sunlight the tint does all the work — the colour-only
   failure the badge below exists to cover. */
.tiles input:checked + .tile{border-color:var(--accent);
                             background:color-mix(in srgb,var(--accent) 10%,var(--card))}
.tiles input:focus-visible + .tile{outline:2px solid var(--focus);outline-offset:2px}
/* Selection is signalled THREE ways — border, tint, and this badge — because a
   tinted border alone is colour-only, invisible to a colour-blind driver and
   washed out in sun. The badge is a SHAPE that is simply absent when
   unselected, so the difference survives greyscale. aria-checked is the third.
   Positioned absolutely so its arrival cannot shift the art a pixel. */
.tile .badge{position:absolute;top:8px;right:8px;opacity:0;
             width:18px;height:18px;color:var(--accent)}
.tiles input:checked + .tile .badge{opacity:1}
.tile .art{width:44px;height:24px;stroke:var(--dim);fill:none;stroke-width:1.6;
           stroke-linecap:round;stroke-linejoin:round}
.tiles input:checked + .tile .art{stroke:var(--accent)}
.tile .text{display:flex;flex-direction:column;min-width:0}
.tile .name{margin:8px 0 0;font-size:12px;line-height:1.35;font-weight:700;
            letter-spacing:.6px;text-transform:uppercase;color:var(--text)}
.tiles input:checked + .tile .name{color:var(--accent)}
.tile .clarifier{margin:4px 0 0;font-size:13px;line-height:1.35;color:var(--dim)}

/* ── Feedback ─────────────────────────────────────────────────────────────── */
.alert{display:flex;align-items:flex-start;gap:10px;
       border-radius:10px;padding:12px 14px;margin:16px 0 0;font-size:14px}
.alert svg{flex:none;width:16px;height:16px;margin-top:3px;
           fill:none;stroke:currentColor;stroke-width:1.8;
           stroke-linecap:round;stroke-linejoin:round}
.alert.error{color:var(--red);background:color-mix(in srgb,var(--red) 10%,transparent);
             border:1px solid color-mix(in srgb,var(--red) 30%,transparent)}
.alert.notice{color:var(--green);background:color-mix(in srgb,var(--green) 10%,transparent);
              border:1px solid color-mix(in srgb,var(--green) 30%,transparent)}
.alert[hidden]{display:none}

/* ── Buttons ──────────────────────────────────────────────────────────────── */
.btn{display:inline-flex;align-items:center;justify-content:center;
     background:var(--accent);color:#06121F;font-weight:650;text-decoration:none;
     /* 34px. Was 48, then 40, and still read oversized on desktop — these are
        ordinary controls in a dense table, not hero CTAs. Comfortably above the
        WCAG 2.2 24x24 target minimum. `.btn.block` (the sign-in CTA) keeps its
        own larger size below: a primary full-width action is meant to be big. */
     padding:6px 13px;border-radius:8px;font-size:13.5px;min-height:34px;
     border:0;cursor:pointer;font-family:inherit}
.btn:hover{filter:brightness(1.08)}
/* offset:3px, not the shared 2px: on this filled blue the #8FD3FF ring is only
   1.52:1 against the button's own #4DABF7 fill and effectively invisible.
   3px lifts it clear onto the page background (11.91:1). Matches support.html. */
.btn:focus-visible{outline:2px solid var(--focus);outline-offset:3px}
/* Full-width primary action (sign-in, reset). Was 48px and read as oversized
   beside the 34px controls everywhere else; 40 keeps it clearly the primary
   action without dominating a short form. */
.btn.block{display:flex;width:100%;margin:20px 0 16px;min-height:40px;font-size:14.5px;padding:10px 18px}
.btn[disabled]{background:var(--line);color:var(--dim);cursor:default;filter:none}

/* Variants. Each sets its OWN background: they used to sit on a page-local
   `.btn` with a neutral card fill, and when that duplicate was deleted a
   "Remove" button inherited the accent blue and became blue-on-blue. */
.btn-secondary,.btn-danger{background:var(--card);border:1px solid var(--line)}
.btn-secondary{color:var(--text)}
.btn-primary{background:var(--accent);border-color:var(--accent);color:#06121F}
.btn-danger{color:var(--red);border-color:color-mix(in srgb,var(--red) 45%,transparent)}
.btn-danger:hover{background:color-mix(in srgb,var(--red) 12%,var(--card))}
.btn-sm{padding:4px 10px;min-height:29px;font-size:12.5px}

.alt-row{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
         gap:2px;color:var(--dim);font-size:14px}

/* Spinner for the in-flight submit. Honours reduced motion — a driver who has
   asked the OS to stop animation should not get a spinner regardless. */
.spinner{width:18px;height:18px;border:2px solid currentColor;border-top-color:transparent;
         border-radius:50%;animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
  .spinner{animation:none}
  .toggle label{transition:none}
}

/* Visually hidden but present for screen readers — used for the live region
   that announces errors, and for the legend on the account-type radio group. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
         overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ── Signed-in shell, for the console's other pages ───────────────────────── */
.shell-nav{display:flex;flex-wrap:wrap;gap:4px;
           border-bottom:1px solid var(--line);margin:0 0 32px}
.shell-nav a{display:inline-flex;align-items:center;min-height:44px;padding:0 14px;
             color:var(--dim);text-decoration:none;font-size:15px;font-weight:600;
             border-bottom:2px solid transparent;margin-bottom:-1px}
.shell-nav a:hover{color:var(--text)}
.shell-nav a[aria-current=page]{color:var(--text);border-bottom-color:var(--accent)}
.who{display:flex;flex-wrap:wrap;align-items:center;gap:12px;color:var(--dim);font-size:14px}

/* ── Responsive ───────────────────────────────────────────────────────────────
   Three widths are checked: 393 (phone), 820 (tablet), 1440 (desktop). Only
   the phone needs a rule — the card is max-width constrained, so 820 and 1440
   are the same centred column with more air around it, which is the intent. */
@media (max-width:520px){
  main{padding:32px 0 48px}
  .auth-wrap,.wrap{padding:0 16px}
  /* Form controls go full width and the role tiles STACK. Side by side at
     393px each tile is ~166px wide, and "I'm the one driving" wraps to three
     lines in English — worse in Spanish and Punjabi, which the app measured at
     roughly twice the English width. Stacked, each tile has the full column.
     The truck-count comparison is weaker stacked, so the art stays and the
     tiles keep equal height; the clarifier text carries more of the load. */
  .tiles{flex-direction:column}
  .tile{flex-direction:row;justify-content:flex-start;text-align:left;gap:14px;
        padding:12px 14px}
  .tile .art{margin:0}
  .tile .text{display:flex;flex-direction:column}
  .tile .name{margin:0}
  .btn,.btn.block{width:100%}
  .alt-row{justify-content:center}
  footer .wrap{flex-direction:column;gap:4px}
}

/* ── Footer ──────────────────────────────────────────────────────────────
   Every console page, not just sign-in: Terms, Privacy, Support and account
   deletion must be reachable from wherever a driver is, and both stores are
   one tap away. Store links are rewritten by store-links.js. */
/*
  STICKY FOOTER. `margin-top` alone left the footer floating directly under
  short content — on the map's empty state it sat mid-screen with a page of
  blank space beneath it, which reads as a broken layout rather than the end
  of the page.

  The body is a flex column at least as tall as the viewport, and `main` grows
  to absorb the slack, so the footer lands on the fold when content is short
  and flows normally when it is long. `min-height:0` keeps a flex child from
  refusing to shrink below its content.
*/
body { display: flex; flex-direction: column; min-height: 100vh; }
/*
  `width:100%` is REQUIRED, not decoration. `body` is a flex column, and these
  wraps carry `margin: 0 auto` for centring — but an auto margin on a flex item
  cancels the default stretch, so the column shrink-wrapped to its content:
  Account measured 611px against its own 1080px cap, leaving a 341px content
  pane. `width:100%` restores the intended width, and `max-width` still caps it.
*/
body > main, body > .wrap:not(.site-foot) { flex: 1 0 auto; min-height: 0; width: 100%; }
.site-foot { flex-shrink: 0; border-top: 1px solid var(--line); margin-top: 24px; padding: 10px 0 12px; }
/*
  The 44px touch target below is a POINTER-COARSE requirement, not a universal
  one. Applied at every width it made a one-line footer a 110px band, most of
  it empty. Trimmed for mouse input only — touch and stylus never match
  `pointer: fine`, so the 44px base rule above still stands there, where a
  mis-tap is the real cost.
*/
@media (pointer: fine) {
  .site-foot .foot-links a, .site-foot .foot-store-link { min-height: 30px; }
}
/*
  The 96px `padding-bottom` each page put on its content wrap was clearance for
  the MOBILE tab bar, which is fixed only under 560px — but it applied at every
  width, so on desktop it stacked with the footer's own margin and left ~144px
  of dead space above the rule. Neutralised here for wide viewports; the mobile
  media query below restores the clearance where the tab bar actually exists.
*/
/* (per-page `.wrap` padding-bottom is corrected in each page's own rule) */
/*
  The copyright used to strand.

  `footer .wrap` sets justify-content: space-between, and .foot-inner IS that
  element. With three children on one line that reads fine; the moment the
  viewport narrows enough for the third to wrap, space-between centres the lone
  survivor on its own row — it landed at x=969 in a 1543px viewport, aligned to
  nothing. Wider pages made it wrap sooner, which is why the sign-in redesign
  surfaced it.

  Links and store buttons stay together at the start; the copyright is pushed
  to the end by its own margin, so when it wraps it wraps to the LEFT edge like
  every other row rather than floating.
*/
/*
  The mode radios are STATE, not a control.

  index.js drives them (setMode, the ?mode= boot, the ?ref= boot) and reads
  their checked state, so they cannot be removed — but the visible tab bar is
  gone. `clip` rather than display:none: the inputs stay focusable and stay in
  the accessibility tree as a radiogroup, so a keyboard or screen-reader user
  can still reach the choice. display:none would delete them from both.
*/
.toggle-state{position:absolute;width:1px;height:1px;overflow:hidden;
              clip:rect(0 0 0 0);white-space:nowrap}

/*
  `footer .wrap` is (0,1,1) and beat a bare `.foot-inner` (0,1,0), so the first
  attempt at this silently kept space-between. Matched specificity here.

  Two things were wrong. The copyright STRANDED: with three children on one
  line space-between reads fine, but the moment the third wraps it centres the
  lone survivor on its own row — measured at x=819 in a 1600px viewport,
  aligned to nothing. And the footer was capped at --max-narrow (760px) while
  the sign-in page is 1180px, so it did not span the page it sits under.

  Now: the footer shares the auth page's width, the three groups sit left to
  right, and the copyright is pushed to the end by its own margin so that when
  it does wrap it wraps to the LEFT edge like any other row.
*/
/*
  ── ONE WIDTH FOR THE AUTH PAGE ────────────────────────────────────────────
  The header, the content and the footer must line up, and they did not: the
  shared `.wrap` is --max (760px) while the sign-in split is 1180px, so the
  logo sat 116px inside where the form began and "Back to site" stopped 190px
  short of where the panel ended. Measured, not eyeballed.

  Scoped to body.auth-page rather than changing --max, because every other
  console page is a 760px reading column and widening them all would be a
  different change to a dozen screens. --auth-max is declared once here so the
  three rules cannot drift apart again.
*/
body.auth-page{--auth-max:1180px;--auth-pad:clamp(16px,4vw,40px)}
/*
  ONE padding value, declared once. The three rules previously repeated the
  clamp, and a later `.auth-wrap,.wrap{padding:0 16px}` in the narrow media
  query overrode it for the HEADER only — so at 375px the header stopped at
  342 while the card reached 347. Both edges now resolve from --auth-pad, and
  the !important is what keeps the later shared rule from splitting them again.
*/
body.auth-page header .wrap{max-width:var(--auth-max);padding:0 var(--auth-pad)!important}
body.auth-page .auth-split{max-width:var(--auth-max);padding:0 var(--auth-pad)}
body.auth-page footer .wrap{max-width:var(--auth-max);padding:0 var(--auth-pad)!important}

footer .wrap.foot-inner { display: flex; flex-wrap: wrap; align-items: center;
                          gap: 12px 24px; justify-content: flex-start; }
footer .wrap.foot-inner .foot-copy { margin-left: auto; }
@media (max-width: 560px) {
  /*
    Phones only. Centring looked right at 820 until the header and content were
    aligned to one edge — then a tablet had its logo at x=31 and its footer
    links at x=167, which reads as a mistake. Below 560 the rows are narrow
    enough that centred is the better shape, and an auto margin would still
    shove the copyright right on its own row.
  */
  footer .wrap.foot-inner { justify-content: center; text-align: center; }
  footer .wrap.foot-inner .foot-copy { margin-left: 0; flex-basis: 100%; }
}
.foot-links { display: flex; flex-wrap: wrap; gap: 4px 18px; }
.foot-links a, .foot-store-link { color: var(--dim); text-decoration: none; font-size: 13.5px;
  display: inline-flex; align-items: center; min-height: 44px; }
.foot-links a:hover, .foot-store-link:hover { color: var(--text); text-decoration: underline; }
.foot-get { display: flex; align-items: center; gap: 12px; }
.foot-get-label { color: var(--dim); font-size: 13.5px; }
/*
  36px was overriding the 44px the rule above sets for BOTH footer link kinds,
  so on a touch device the iOS/Android buttons stayed under the target size
  while the plain footer links were fine — and the `pointer: coarse` block far
  below could not win it back at equal specificity. Verified on a 768 frame
  with `pointer: fine` disabled: links 44px, these 36px. The pointer:fine rule
  above is what shrinks BOTH for a mouse; this one no longer fights it.
*/
.foot-store-link { border: 1px solid var(--line); border-radius: 8px; padding: 0 12px; }
/* The other platform stays clickable — someone may be sending the link on. */
.foot-store-link.other-store { opacity: .65; }
.foot-copy { color: var(--dim); font-size: 13px; margin-left: auto; }
@media (max-width: 560px) {
  /* The bottom tab bar is fixed on mobile; keep the footer clear of it. */
  .site-foot { margin-bottom: 96px; }
  .foot-copy { margin-left: 0; width: 100%; }
  /*
    OVERLAPPING TAP TARGETS — measured 22px of overlap between "Delete account"
    and BOTH "Home" and "Support" on the live sign-in page at 375px, so the top
    of a "Home" tap landed on ACCOUNT DELETION.

    The cause is the `footer a { margin:-13px … }` above. That negative margin
    is deliberate and correct on ONE line: padding raises the hit box to 44px
    and the margin absorbs the extra height so the footer keeps its rhythm.
    But it collapses each box to ~18px of LAYOUT height while the hit area
    stays 44px — so as soon as the links wrap, which they do on any phone, the
    rows' hit areas grow into each other.

    Here the rhythm argument no longer applies (the footer is already stacked),
    so the vertical margin is dropped and the row gap absorbs the real height.
    The 44px target itself is not negotiable — shrinking it to make the rows
    fit would trade a mis-tap for a miss.
  */
  .site-foot .foot-links a { margin-top: 0; margin-bottom: 0; }
  .foot-links { gap: 8px 18px; }
  /*
    TOUCH TARGETS ON A PHONE. `.btn` (34px) and `.btn.block` (40px) are sized
    for a mouse, and the comments above explain why — at a desk they read as
    oversized otherwise, and both clear the WCAG 2.2 24x24 minimum.

    That reasoning is about a pointer. This app is used one-handed in a cab,
    in glare, sometimes through a glove, and 24x24 is the accessibility FLOOR
    for a mouse, not a comfortable thumb target. Apple and Google both ask for
    44/48px. Raised here only where the input is a thumb; the desktop sizes
    above are untouched.
  */
  .btn { min-height: 44px; }
  .btn.block { min-height: 48px; }
  /* Same reasoning for the footer's iOS/Android badges (36px by default). */
  .foot-store-link { min-height: 44px; }
}

/* ── Console header ──────────────────────────────────────────────────────
   ONE definition, here, because there were FOUR — one inlined per page — and
   they had drifted: fleet/account hid the tab icons (`display:none`) and
   pushed the links right (`margin-right:auto` on the brand) while
   dashboard/map showed icons left-aligned. The header visibly changed shape
   as you moved between pages. Page-local `.nav` rules are gone; add to this
   block instead. */
/*
  FULL-BLEED BAR, CONSTRAINED CONTENTS. The bar's border and background span
  the viewport, but its links line up with the page column underneath.

  Measured before this change: at 2560px the brand sat 1174px to the LEFT of
  the content edge, because `.nav` had no max-width while every `.wrap` caps
  at 1200 (860 on Account). The header read as a separate object floating
  above the page rather than its header.

  `--nav-max` matches `.wrap`'s cap; account.html overrides it to its own.
*/
.nav { display: flex; background: var(--raised); border-bottom: 1px solid var(--line); }
/* 56px on a raised surface (redesign §01). The bar's height is set here, not
   by the 44px links plus padding, so the pill geometry below is exact. */
.nav-inner { display: flex; align-items: center; gap: 2px; height: 56px; padding: 0 20px;
  width: 100%; max-width: var(--nav-max, 1200px); margin: 0 auto; }
/*
  The wordmark is a LABEL, not body text. It was a bare <span> whose text
  highlighted blue on a stray double-click in the nav (owner report,
  2026-09-08) — a selection nobody can act on, over a word that looks like it
  should be clickable. It is now an <a> home link on every signed-in page
  (console/index.html already was one), so the affordance matches the
  behaviour, and the label itself is unselectable.
*/
.nav .brand { display: inline-flex; align-items: center; gap: 9px; font-weight: 700;
  font-size: 15.5px; letter-spacing: -.01em; color: var(--text); margin-right: 22px;
  -webkit-user-select: none; user-select: none; text-decoration: none; }
a.brand:hover { color: var(--text); }
/* The route on a trip card — quieter than the title, louder than the ident. */
.trip-route { margin: 2px 0 0; font-size: 13.5px; color: var(--text-dim, #9fb0c0); }
a.brand:focus-visible { outline: 2px solid var(--accent, #4da3ff); outline-offset: 3px;
  border-radius: 6px; }
.nav .brand img { width: 22px; height: 22px; border-radius: 6px; }
/*
  A 34px pill inside a 44px hit box. The link keeps `min-height:44px` (touch
  target — see the rule above `.back`), and the visible pill is painted by a
  pseudo-element inset 5px, so the tab LOOKS like the 34px tab in the mock
  while the target stays 44. `isolation` keeps the z-index:-1 pseudo inside
  the link rather than behind the bar.
*/
.nav a { position: relative; isolation: isolate; color: var(--dim); text-decoration: none;
  font-size: 14px; font-weight: 500; display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 12px; border-radius: 8px; }
.nav a::before { content: ""; position: absolute; inset: 5px 0; border-radius: 8px; z-index: -1; }
.nav a:hover { color: var(--text); }
.nav a:hover::before { background: var(--card); }
/*
  SHAPE, not just weight. The active tab was signalled by font-weight and a
  lighter grey alone — the weakest pair of cues available, and both are lost
  to anyone who cannot resolve a 100-unit weight difference at 14px. The
  filled pill plus an underline anchored to the bar's own border adds shape
  and position, so the signal survives greyscale and low vision.
*/
.nav a[aria-current="page"] { color: var(--text); font-weight: 600; }
.nav a[aria-current="page"]::before { background: var(--raised-2); }
/* The link is 44 tall centred in the 56 bar (6px above and below); -6px puts
   the 2px underline flush on the bar's bottom border, as in the mock. */
.nav a[aria-current="page"]::after { content: ""; position: absolute;
  left: 12px; right: 12px; bottom: -6px; height: 2px;
  background: var(--accent); border-radius: 2px; }
/* Stroke icons, not emoji: emoji render differently on Windows, macOS and
   Android; a line icon looks the same everywhere. */
.nav .tab-icon { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.nav-inner #sign-out { margin-left: auto; }

@media (max-width: 560px) {
  /* Bottom tab bar on phones — the same five destinations, thumb-reachable. */
  .nav { position: fixed; inset: auto 0 0; border-top: 1px solid var(--line);
    border-bottom: 0; background: #080C12; z-index: 20; }
  /* The bar is thumb-reachable full width; the desktop column cap would
     bunch five tabs into the middle on a wide phone/tablet. */
  .nav-inner { max-width: none; height: auto; justify-content: space-around;
    gap: 0; padding: 6px 0 8px; }
  .nav .brand { display: none; }
  .nav a, .nav-inner #sign-out { margin-left: 0; flex-direction: column; gap: 3px;
    font-size: 11px; padding: 0 6px; white-space: nowrap; }
  /* 6px, not 10: a fleet owner has SIX tabs, and at 375px "Sign out" wrapped
     onto two lines and sat taller than its neighbours. nowrap keeps a label
     one line whatever the width. */
  /* The whole 44px target is the pill here — there is no 56px bar to inset from. */
  .nav a::before { inset: 0; }
  /* No bottom border to sit on in the fixed bar — the pill carries it alone. */
  .nav a[aria-current="page"]::after { display: none; }
  .nav .tab-icon { width: 20px; height: 20px; }
}

/* ── Redesign port (from _prototype.html's polish pass) ─────────────────
   Shared primitives the redesigned pages use. Tokens only; nothing new.
   Sizes the pages define locally (.stat, .hero, text floors) are raised in
   each page's own <style>: a page-local rule outranks anything here by
   source order, so a shared override would silently lose. */

/* A card's action row: ONE bordered primary, the rest quiet text links.
   No colour-only cue — the primary differs in fill, weight and shape. */
.trip-foot, .job-foot { display: flex; gap: 8px 14px; flex-wrap: wrap; align-items: center;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.trip-foot .open, .job-foot .open { font-weight: 600; text-decoration: none; font-size: 14px;
  display: inline-flex; align-items: center; min-height: 34px; gap: 4px; }
.trip-foot .open:hover, .job-foot .open:hover { text-decoration: underline; }
.trip-foot .open.is-primary { background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius: 8px; padding: 0 12px; font-weight: 700; }
.trip-foot .open.is-primary:hover { background: color-mix(in srgb, var(--accent) 22%, transparent);
  text-decoration: none; }
.trip-foot .open.is-secondary { color: var(--body); font-weight: 500; }
.trip-foot .open.is-secondary:hover { color: var(--accent); }
.btn-outline { background: transparent; color: var(--accent); border: 1px solid var(--accent); }
.btn-outline:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); filter: none; }
.trip-foot .open:focus-visible, .job-foot .open:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 8px; }
@media (max-width: 560px) {
  .trip-foot .open, .job-foot .open { min-height: 44px; }
  .trip-foot .open.is-primary { flex-basis: 100%; justify-content: center; }
  .trip-foot, .job-foot { gap: 10px 14px; }
}

/* Permit validity is safety information, not a date: a chip with a dot,
   so the state survives glare and greyscale. Amber is the pages' existing
   warning colour (`.when.soon`); an expired permit gets the red banner. */
.perm-state { display: inline-flex; align-items: center; gap: 6px; font-size: 13.5px;
  font-weight: 700; padding: 3px 9px; border-radius: 999px; border: 1px solid transparent;
  white-space: nowrap; }
.perm-state::before { content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; flex: none; }
.perm-state.ok { color: var(--green);
  background: color-mix(in srgb, var(--green) 12%, transparent);
  border-color: color-mix(in srgb, var(--green) 34%, transparent); }
.perm-state.soon { color: #E3A008;
  background: color-mix(in srgb, #E3A008 12%, transparent);
  border-color: color-mix(in srgb, #E3A008 34%, transparent); }
.perm-state.none, .perm-state.expired { color: var(--dim);
  background: color-mix(in srgb, var(--dim) 12%, transparent);
  border-color: color-mix(in srgb, var(--dim) 30%, transparent); }

/* Mobile table → labelled cards. A table with class `cards-sm` and `data-l`
   on its cells stacks under 560; the label prints from the attribute, so
   money is never clipped by a sideways scroller. (`table.roster` keeps its
   own copy of the pattern in fleet.html, on .cardlabel spans.) */
@media (max-width: 560px) {
  table.cards-sm, table.cards-sm tbody, table.cards-sm tr, table.cards-sm td { display: block; width: 100%; }
  table.cards-sm thead { display: none; }
  table.cards-sm tr { border: 1px solid var(--line); border-radius: 10px; background: var(--bg);
    padding: 10px 12px; margin: 0 0 8px; }
  table.cards-sm td { border-top: 0; padding: 3px 0; display: flex; align-items: center;
    justify-content: space-between; gap: 14px; text-align: right; }
  table.cards-sm td:first-child { font-weight: 700; text-align: left; padding-bottom: 6px;
    margin-bottom: 4px; border-bottom: 1px solid var(--line-soft); }
  table.cards-sm td[data-l]::before { content: attr(data-l); flex: none; color: var(--dim);
    font-size: 11.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; text-align: left; }
  table.cards-sm td[data-l]:empty { display: none; }
  table.cards-sm td.num { white-space: normal; }
}

/* ── Plan badge — .nav-plan (nav.js) ────────────────────────────────────────────────
   Which membership this account holds, on every page, as a link to the
   Subscription panel — seeing your plan and wanting to change it are the
   same moment. Starts `hidden`; nav.js fills it only once the tiers are
   KNOWN, so a paying driver never sees "Free" flash while the read is out.
   Desktop: a 28px chip in the right-hand cluster beside Sign out, inside the
   link's existing 44px hit box. `.is-free` is the accent outline: Free is the
   one plan that reads as an offer rather than a status. */
.nav a.nav-plan { margin-left: auto; padding: 0 11px; font-size: 12.5px; font-weight: 600;
  letter-spacing: .01em; color: var(--text); white-space: nowrap; }
.nav a.nav-plan::before { inset: 8px 0; border: 1px solid var(--line); background: var(--card); }
.nav a.nav-plan:hover::before { background: var(--raised-2); }
.nav a.nav-plan.is-free { color: var(--accent); }
.nav a.nav-plan.is-free::before { border-color: var(--accent); background: transparent; }
.nav a.nav-plan.is-free:hover::before { background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* The chip carries its own margin-left:auto so Sign out sits flush beside it. */
.nav-inner .nav-plan + #sign-out { margin-left: 4px; }

@media (max-width: 560px) {
  /* PHONE. The brand row is gone and the nav is the fixed bottom tab bar, so
     the badge becomes a one-line strip along the TOP of that bar, above the
     tabs: full width, centred, thumb-reachable, and never over page content
     (a floating top-right chip collided with the greeting h1 at 375px, and an
     eighth tab does not fit beside seven). Visible height 30px; the hit box
     is grown to 44 with a pseudo that reaches into the page's clearance
     padding above the bar, so a tap there never lands on a tab. The extra
     30px stays inside the 92–112px clearance every page keeps for the bar. */
  .nav-inner:has(.nav-plan:not([hidden])) { flex-wrap: wrap; }
  .nav a.nav-plan { flex: 0 0 100%; order: -1; flex-direction: row; justify-content: center;
    gap: 6px; min-height: 30px; margin: -6px 0 4px; padding: 0 12px; font-size: 12px;
    border-bottom: 1px solid var(--line); border-radius: 0; }
  .nav a.nav-plan::before { inset: 0; border: 0; background: transparent; border-radius: 0; }
  .nav a.nav-plan.is-free::before { border: 0; }
  .nav a.nav-plan::after { content: ""; position: absolute; left: 0; right: 0; top: -14px; bottom: 0; }
  .nav a.nav-plan:hover::before { background: var(--card); }
}

/* ── Tablet header (561–1023px) ────────────────────────────────────────────
   A fleet owner's bar is SEVEN items plus the plan chip and Sign out. With
   icons and 12px pads it measured 965px at 768 (iPad portrait) — the page
   scrolled sideways and Sign out sat off-screen. Icons go and pads tighten
   here only; the phone bar (≤560) and desktop are untouched. Measured after:
   "Fleet · up to 20 seats" owner fits at 768 with room to spare. */
@media (min-width: 561px) and (max-width: 1023px) {
  .nav .tab-icon { display: none; }
  .nav a { padding: 0 8px; font-size: 13.5px; }
  .nav .brand { margin-right: 10px; }
  /* Below ~720 a 20-seat owner's bar still does not fit on one line; the
     chip and Sign out drop to a second row rather than pushing the page
     sideways. Height is min-, not fixed, so the row can grow. */
  .nav-inner { flex-wrap: wrap; height: auto; min-height: 56px; }
}

/* ── Coarse pointer ────────────────────────────────────────────────────────
   The 44px targets above are gated on ≤560px, but an iPad is 768–1024 wide
   and still a thumb. Same sizes, keyed on the input instead of the width, so
   a desktop with a mouse keeps its 34px controls. (`.btn` here outranks
   `.btn-sm` by source order, deliberately — same as the ≤560 block.) */
@media (pointer: coarse) {
  .btn { min-height: 44px; }
  .btn.block { min-height: 48px; }
  .foot-store-link { min-height: 44px; }
  .trip-foot .open, .job-foot .open { min-height: 44px; }
}
