/*
  chiptuning.ai — CANONICAL UI THEME (single source of truth).
  Purple-on-black, token-driven. Change ONE value, --accent-rgb (in the brand
  layer near the end), to recolour the whole UI. Supersedes the old red
  X-Portal app.css (Toxic Tuning). Every chiptuning.ai app should use THIS file.
  Owner of this file: the Identity session; keep in sync with the id login.
*/
/* =============================================================================
   X-Portal — app.css
   Ported from the approved prototype (Ultratec theme).
   ground #141417 · cards #222 · accent #e10600 · Plus Jakarta Sans + Roboto Mono
   Dark-only by design. Borderless: separation is elevation and fill, not rules.
   ============================================================================= */

@import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&family=Roboto+Mono:wght@400;500;600&display=swap");

/* ============================================================
   Toxic Tuning portal — themed to the Ultratec template
   ground #141417 · cards #222 · borders #333 · accent #e10600
   Dark-only, like the template. Every colour painted explicitly.
   ============================================================ */
:root {
  --bg:        #141417;
  --card:      #222222;
  --card-2:    #2a2a2a;
  --card-3:    #303030;
  --line:      #333333;
  --line-2:    #2b2b2b;
  --ink:       #ffffff;
  --muted:     #b1b1b1;
  --muted-2:   #7c7c7c;

  /* Channels, so every translucent red derives from one place instead of
     ten copies of the same rgba() drifting apart. */
  --red-rgb:   225, 6, 0;
  --red:       rgb(var(--red-rgb));
  --red-soft:  rgba(var(--red-rgb), .13);
  --scrim-rgb: 255, 255, 255;
  --red-ink:   #ff6b60;
  --green-ink: #34c759;
  --amber-ink: #ff9f0a;
  --blue-ink:  #3b9dff;
  --green:     #28a745;
  --amber:     #ff9f0a;
  --blue:      #3b9dff;
  --grey:      #6c757d;

  --rail:      #0d0d10;
  --rail-hover:rgba(var(--scrim-rgb), .045);
  --sidebar-w: 250px;
  --topnav-h:  60px;
  --r:         12px;
  --elev:      0 1px 2px rgba(0,0,0,.45), 0 12px 32px -18px rgba(0,0,0,.9);
  --elev-lo:   0 1px 3px rgba(0,0,0,.4);
  --fill:      rgba(var(--scrim-rgb), .055);
  --fill-2:    rgba(var(--scrim-rgb), .09);
}

/* =============================================================================
   Themes
   =============================================================================

   Three states. No cookie means follow the system, so the media query owns the
   default; an explicit choice stamps data-theme on <html> and beats it in both
   directions. Every rule below redefines tokens only -- no component styling
   lives in here, because a colour whose sole definition sits inside a media
   query is invisible in the state that query does not match.

   color-scheme is set alongside so the browser's own furniture -- scrollbars,
   select menus, form controls -- follows the same choice. */

:root { color-scheme: dark; }

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
  /* Surfaces run the other way up: the ground is the darkest neutral and
     cards are white, so elevation still reads as "lighter than what is
     behind it" without inverting every shadow in the sheet. */
  --bg:        #f1f1f4;
  --card:      #ffffff;
  --card-2:    #f4f4f8;
  --card-3:    #e8e8ef;
  --line:      #e0e0e7;
  --line-2:    #ececf1;
  --ink:       #17171c;
  --muted:     #52525d;
  --muted-2:   #65656f;   /* 4.68:1 on the rail, the darkest light surface */

  --scrim-rgb: 0, 0, 0;

  /* Chips keep their saturated colour; the -ink variants darken so the same
     meaning is still legible as text on white. */
  --red-ink:   #b20500;
  --green-ink: #1a7f37;
  --amber-ink: #9a5b00;
  --blue-ink:  #1263c4;
  --grey:      #6c757d;

  --rail:      #e7e7ed;
  --rail-hover:rgba(var(--scrim-rgb), .05);

  /* A near-black shadow reads as grime on white. Light needs a shorter, much
     softer drop plus a faint outline to hold the card edge. */
  --elev:      0 1px 2px rgba(23,23,28,.06), 0 10px 24px -16px rgba(23,23,28,.28),
               0 0 0 1px rgba(23,23,28,.05);
  --elev-lo:   0 1px 2px rgba(23,23,28,.07), 0 0 0 1px rgba(23,23,28,.045);
  --fill:      rgba(var(--scrim-rgb), .05);
  --fill-2:    rgba(var(--scrim-rgb), .085);
  }
}

:root[data-theme="light"] {
  color-scheme: light;
  /* Surfaces run the other way up: the ground is the darkest neutral and
     cards are white, so elevation still reads as "lighter than what is
     behind it" without inverting every shadow in the sheet. */
  --bg:        #f1f1f4;
  --card:      #ffffff;
  --card-2:    #f4f4f8;
  --card-3:    #e8e8ef;
  --line:      #e0e0e7;
  --line-2:    #ececf1;
  --ink:       #17171c;
  --muted:     #52525d;
  --muted-2:   #65656f;   /* 4.68:1 on the rail, the darkest light surface */

  --scrim-rgb: 0, 0, 0;

  /* Chips keep their saturated colour; the -ink variants darken so the same
     meaning is still legible as text on white. */
  --red-ink:   #b20500;
  --green-ink: #1a7f37;
  --amber-ink: #9a5b00;
  --blue-ink:  #1263c4;
  --grey:      #6c757d;

  --rail:      #e7e7ed;
  --rail-hover:rgba(var(--scrim-rgb), .05);

  /* A near-black shadow reads as grime on white. Light needs a shorter, much
     softer drop plus a faint outline to hold the card edge. */
  --elev:      0 1px 2px rgba(23,23,28,.06), 0 10px 24px -16px rgba(23,23,28,.28),
               0 0 0 1px rgba(23,23,28,.05);
  --elev-lo:   0 1px 2px rgba(23,23,28,.07), 0 0 0 1px rgba(23,23,28,.045);
  --fill:      rgba(var(--scrim-rgb), .05);
  --fill-2:    rgba(var(--scrim-rgb), .085);
}

:root[data-theme="dark"] { color-scheme: dark; }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Plus Jakarta Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.mono { font-family: "Roboto Mono", ui-monospace, Menlo, Consolas, monospace; font-variant-numeric: tabular-nums; }

.eyebrow {
  font-size: 11.5px; font-weight: 500; text-transform: uppercase;
  letter-spacing: .08em; color: var(--muted-2);
}

/* ---------------- Top nav ---------------- */
.top-nav {
  position: fixed; top: 0; left: 0; right: 0; height: var(--topnav-h); z-index: 1000;
  background: var(--rail); box-shadow: 0 4px 18px -10px rgba(0,0,0,.9);
  display: flex; align-items: center; gap: 28px; padding: 0 20px 0 22px;
}
/* The logo is a red block with the wordmark knocked out of it, used as
   supplied. It carries its own background, so it needs no colour from the
   theme and reads the same on the dark rail and the light one. */
.logo { display: flex; align-items: center; flex: none; }
.logo img { display: block; height: 26px; width: auto; }
@media (max-width: 760px) { .logo img { height: 21px; } }
.nav-meta { display: flex; align-items: center; gap: 20px; font-size: 14px; color: var(--muted); }

/* One label-and-value pair. The pair holds together tightly; the space between
   pairs is what separates them, so the gap does the grouping. */
.nav-meta .meta { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.nav-meta .lbl { color: var(--muted-2); font-size: 13px; }

.nav-status { flex: none; }

/* Balance last, tight against the sign-out button. */
.nav-stats { flex: none; gap: 18px; margin-right: 2px; }
.pill {
  display: inline-block; padding: 3px 12px; border-radius: 100px;
  font-size: 12.5px; font-weight: 500; color: #fff; background: var(--green);
}
.pill.n { background: var(--fill-2); font-family: "Roboto Mono", monospace; }
.pill.warn { background: var(--red); }
.pill.busy { background: var(--amber); color: #1a1a1a; }
.pill.closed { background: var(--grey); }
/* Available is the same green as Open: one meaning, one colour. Unavailable
   is the same grey as Closed. */
.pill.ai:not(.closed) { background: var(--green); }

.nav-meta .clock { display: inline-flex; align-items: center; gap: 7px; color: var(--muted); }
.nav-meta .clock svg { width: 15px; height: 15px; opacity: .75; }
.nav-meta .countdown {
  font-size: 13px; color: var(--green-ink);
  font-variant-numeric: tabular-nums;
}
.nav-meta .countdown.shut { color: var(--amber-ink); }

/* Shed the header from the outside in: the optional pair first, then the
   labels, then the clock — the balance is the last thing to go. */
@media (max-width: 1180px) { .nav-meta .hide-sm { display: none; } }
@media (max-width: 1040px) { .nav-status .lbl { display: none; } }
@media (max-width: 940px)  { .nav-meta { gap: 14px; font-size: 13px; }
                             .nav-stats { gap: 14px; } }
@media (max-width: 820px)  { .nav-status .clock { display: none; } }
@media (max-width: 700px)  { .nav-status { display: none; } }
.top-nav .grow { flex: 1; min-width: 16px; }
/* Icon buttons sit together, not a group apart. */
.nav-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.icon-btn {
  width: 34px; height: 34px; border-radius: 9px; border: 0; padding: 0;
  background: var(--fill); color: var(--muted); cursor: pointer;
  display: grid; place-items: center; position: relative;
}
.icon-btn:hover { background: var(--fill-2); color: var(--ink); }
.icon-btn svg { width: 16px; height: 16px; }
.icon-btn .badge-dot {
  position: absolute; top: -5px; right: -5px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 100px; background: var(--red); color: #fff;
  font-size: 10px; font-weight: 700; display: grid; place-items: center;
  font-family: "Roboto Mono", monospace;
}

/* ---------------- Sidebar ---------------- */
.sidebar {
  position: fixed; top: var(--topnav-h); left: 0; bottom: 0; width: var(--sidebar-w);
  background: var(--rail);
  box-shadow: 4px 0 18px -12px rgba(0,0,0,.9);
  display: flex; flex-direction: column; padding: 14px 0 16px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--scrim-rgb), .13) transparent;
}
.sidebar::-webkit-scrollbar { width: 8px; }
.sidebar::-webkit-scrollbar-thumb { background: rgba(var(--scrim-rgb), .12); border-radius: 100px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.user-card { display: flex; gap: 11px; padding: 6px 14px 18px; align-items: center; }
.user-card .ava {
  width: 40px; height: 40px; border-radius: 9px; flex: none;
  background: var(--fill);
  display: grid; place-items: center; color: var(--muted-2);
}
.user-card .ava svg { width: 21px; height: 21px; }
.user-card .who { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding-top: 2px; }
.user-card .who b { font-size: 14px; font-weight: 500; line-height: 1.25; }
.user-card .who span { font-size: 12px; color: var(--muted-2); }

.sidebar nav { display: flex; flex-direction: column; }
.sidebar .grp { padding: 0 12px; }
.sidebar .grp > .eyebrow { display: block; padding: 18px 8px 8px; font-size: 10.5px; }
.sidebar a {
  display: grid; grid-template-columns: 20px 1fr auto; align-items: center; gap: 12px;
  padding: 9px 11px; border-radius: 8px; margin-bottom: 3px;
  color: var(--muted); text-decoration: none; font-size: 14px;
}
.sidebar a svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.sidebar a:hover { background: var(--rail-hover); color: var(--ink); }
.sidebar a[aria-current="page"] { background: var(--red); color: #fff; font-weight: 500; }
.sidebar a[aria-current="page"] .cnt { background: rgba(255,255,255,.22); color: #fff; }
.sidebar a .cnt {
  font-family: "Roboto Mono", monospace; font-size: 11.5px;
  background: var(--fill); color: var(--muted-2); padding: 2px 8px; border-radius: 100px;
}
.sidebar .spacer { flex: 1; min-height: 10px; }

.oldest {
  margin: 0 12px; padding: 11px 13px; border-radius: 10px;
  background: rgba(var(--scrim-rgb), .04);
  display: flex; flex-direction: column; gap: 2px;
}
.oldest .num {
  font-family: "Roboto Mono", monospace; font-variant-numeric: tabular-nums;
  font-size: 23px; font-weight: 500; color: var(--red); line-height: 1.1;
}
.oldest .num i { font-style: normal; font-size: 12px; color: var(--muted-2); margin-left: 3px; }
.oldest .sub { font-size: 12px; color: var(--muted-2); }

/* ---------------- Main ---------------- */
.main { margin-left: var(--sidebar-w); margin-top: var(--topnav-h); padding: 26px 28px 60px; }
.page-head { margin-bottom: 22px; }
.page-head h1 { margin: 0; font-size: 27px; font-weight: 500; letter-spacing: -.015em; }
.page-head p { margin: 3px 0 0; color: var(--muted-2); font-size: 14px; }

/* Stat cards */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; margin-bottom: 20px; }
.stat {
  background: var(--card); border-radius: var(--r); box-shadow: var(--elev);
  padding: 20px 20px 17px; position: relative; overflow: hidden;
}
.stat b {
  display: block; font-family: "Roboto Mono", monospace; font-variant-numeric: tabular-nums;
  font-size: 32px; font-weight: 500; line-height: 1.05; letter-spacing: -.02em;
}
.stat span { display: block; margin-top: 6px; font-size: 14px; }
.stat small { display: block; margin-top: 2px; font-size: 12.5px; color: var(--muted-2); }
.stat.s-pending  b { color: var(--amber-ink); }
.stat.s-working  b { color: var(--blue-ink); }
.stat.s-returned b { color: var(--green-ink); }
.stat.s-void     b { color: var(--grey); }

/* Alert banner */
.alert {
  display: flex; gap: 14px; align-items: flex-start;
  background: linear-gradient(180deg, rgba(var(--red-rgb),.10) 0%, var(--card) 82%); box-shadow: var(--elev);
  border-radius: var(--r); padding: 15px 18px; margin-bottom: 22px;
}
.alert svg { width: 19px; height: 19px; stroke: var(--red); fill: none; stroke-width: 1.7; flex: none; margin-top: 1px; }
.alert p { margin: 0; font-size: 14px; color: var(--muted); }
.alert strong { color: var(--ink); font-weight: 500; }

/* Cards */
.card { background: var(--card); border-radius: var(--r); margin-bottom: 20px; box-shadow: var(--elev); }
.card > .hd {
  display: flex; align-items: center; gap: 12px;
  padding: 18px 20px 12px;
}
.card > .hd h2 { margin: 0; font-size: 17px; font-weight: 500; }
.card > .hd .cnt {
  font-family: "Roboto Mono", monospace; font-size: 12px;
  background: var(--fill); color: var(--muted-2); padding: 2px 9px; border-radius: 100px;
}
.card > .hd .hint { margin-left: auto; font-size: 12.5px; color: var(--muted-2); }
.card > .bd { padding: 20px; }

/* Table */
.tbl-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
thead th {
  text-align: left; padding: 13px 20px;
  font-size: 11px; font-weight: 500; text-transform: uppercase; letter-spacing: .08em;
  color: var(--muted-2); white-space: nowrap;
}
thead th.r, tbody td.r { text-align: right; }
tbody td { padding: 15px 20px; vertical-align: middle; }
tbody tr { cursor: pointer; }
tbody tr td:first-child { border-radius: 9px 0 0 9px; }
tbody tr td:last-child { border-radius: 0 9px 9px 0; }
tbody tr:nth-child(odd) td { background: rgba(var(--scrim-rgb), .022); }
tbody tr.stale td { background: rgba(var(--red-rgb),.075); }
tbody tr:hover td { background: var(--fill-2); }

td.id { font-family: "Roboto Mono", monospace; font-size: 13.5px; color: var(--muted-2); width: 62px; }
.veh b { display: block; font-size: 15.5px; font-weight: 500; line-height: 1.3; }
.veh span { display: block; font-size: 13px; color: var(--muted-2); }
.plate {
  font-family: "Roboto Mono", monospace; font-size: 12.5px;
  background: var(--fill); border-radius: 6px; padding: 4px 10px;
  color: var(--muted); white-space: nowrap; display: inline-block;
}
td.dealer { font-size: 14px; color: var(--muted); white-space: nowrap; }
.ecu { display: flex; align-items: center; gap: 8px; }
.ecu code {
  font-family: "Roboto Mono", monospace; font-size: 12.5px; color: var(--muted);
  max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block;
}
.tag {
  font-size: 10.5px; font-weight: 500; letter-spacing: .06em;
  padding: 2px 8px; border-radius: 100px; flex: none;
  background: var(--fill); color: var(--muted-2);
}
.tag.tcu { color: var(--blue-ink); background: rgba(59,157,255,.17); }

.wait { font-family: "Roboto Mono", monospace; font-variant-numeric: tabular-nums; white-space: nowrap; }
.wait b { font-size: 20px; font-weight: 500; letter-spacing: -.02em; }
.wait span { display: block; font-family: "Plus Jakarta Sans", Helvetica, sans-serif; font-size: 10.5px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted-2); }
.wait.hot b  { color: var(--red-ink); }
.wait.warn b { color: var(--amber-ink); }
.wait.ok b   { color: var(--muted); }

.badge {
  display: inline-block; font-size: 12px; font-weight: 500;
  padding: 4px 12px; border-radius: 100px; color: #fff; white-space: nowrap;
}
.badge.pending  { background: var(--amber); color: #1a1a1a; }
.badge.working  { background: var(--blue);  color: #1a1a1a; }
.badge.returned { background: var(--green); }
.badge.voidb    { background: var(--grey); }

/* Buttons */
button, .btn {
  font-family: inherit; font-size: 14px; font-weight: 400;
  background: var(--fill); color: var(--ink); border: 0;
  padding: 9px 16px; border-radius: 8px; cursor: pointer;
}
button:hover, .btn:hover { background: var(--fill-2); }
.btn-red { background: var(--red); color: #fff; font-weight: 600; }
.btn-red:hover { background: #ff5147; }
.btn-ghost { background: transparent; }
button:focus-visible, a:focus-visible, tr:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }

/* ---------------- File view ---------------- */
.file-grid { display: grid; grid-template-columns: minmax(0, 1.12fr) minmax(310px, .88fr); gap: 20px; align-items: stretch; }
.back { background: none; border: 0; color: var(--muted); padding: 0 0 14px; font-size: 14px; cursor: pointer; }
.back:hover { color: var(--ink); background: none; }

.seg { display: inline-flex; background: var(--bg); border-radius: 10px; padding: 3px; gap: 3px; }
.seg button { border: 0; background: transparent; color: var(--muted); padding: 6px 15px; border-radius: 6px; font-size: 13.5px; }
.seg button[aria-pressed="true"] { background: var(--red); color: #fff; font-weight: 500; }

textarea {
  width: 100%; min-height: 100px; resize: vertical;
  background: var(--bg); color: var(--ink); border: 0;
  border-radius: 8px; padding: 13px 15px;
  font-family: inherit; font-size: 14.5px; line-height: 1.55;
}
textarea::placeholder { color: var(--muted-2); }
textarea:focus { outline: none; box-shadow: 0 0 0 2px rgba(var(--red-rgb),.6); }

select {
  font-family: inherit; font-size: 13.5px; color: var(--ink);
  background: var(--fill); border: 0; border-radius: 9px; padding: 9px 12px;
}
.composer { display: flex; flex-direction: column; gap: 14px; }
.composer .foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Timeline */
.tl-item { display: grid; grid-template-columns: 10px 1fr; gap: 14px; padding: 16px 0; border-top: 1px solid var(--line-2); }
.tl-item:first-child { border-top: 0; padding-top: 0; }
.tl .mark { width: 9px; height: 9px; border-radius: 50%; margin-top: 7px; background: var(--muted-2); }
.tl .mark.in { background: var(--blue); }
.tl .mark.credit { background: var(--green); }
.tl .mark.charge { background: var(--red); }
.tl .who { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.tl .who b { font-size: 14.5px; font-weight: 500; }
.tl .who time { font-size: 12px; color: var(--muted-2); font-family: "Roboto Mono", monospace; }
.tl p { margin: 5px 0 0; font-size: 14.5px; color: var(--muted); }
.tl p code { font-family: "Roboto Mono", monospace; font-size: 12.5px; color: var(--ink); word-break: break-all; }
.money { display: inline-block; margin-top: 9px; font-family: "Roboto Mono", monospace; font-size: 12.5px; font-weight: 600; padding: 3px 10px; border-radius: 100px; }
.money.up { background: rgba(40,167,69,.15); color: var(--green-ink); }
.money.down { background: var(--red-soft); color: var(--red-ink); }

/* File summary */
.f-title { font-size: 24px; font-weight: 500; line-height: 1.2; letter-spacing: -.015em; text-wrap: balance; }
.f-sub { color: var(--muted-2); font-size: 13.5px; margin-top: 3px; }
.spec { display: grid; grid-template-columns: auto 1fr; gap: 11px 20px; font-size: 14px; margin: 0; }
.spec dt { color: var(--muted-2); font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em; padding-top: 2px; }
.spec dd { margin: 0; }

.gains { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.gain { background: var(--bg); border-radius: 10px; padding: 15px 16px; }
.gain .lbl { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted-2); }
.gain .val { display: flex; align-items: baseline; gap: 8px; margin-top: 6px; font-family: "Roboto Mono", monospace; font-variant-numeric: tabular-nums; }
.gain .from { color: var(--muted-2); font-size: 15px; }
.gain .arr { color: var(--muted-2); font-size: 13px; }
.gain .to { font-size: 24px; font-weight: 500; letter-spacing: -.02em; }
.gain .unit { font-size: 12px; color: var(--muted-2); }
.gain .delta { display: block; margin-top: 5px; font-family: "Roboto Mono", monospace; font-size: 13px; font-weight: 600; color: var(--green-ink); }

.total-row { display: flex; align-items: baseline; justify-content: space-between; }
.total-row .amt { font-family: "Roboto Mono", monospace; font-size: 28px; font-weight: 500; }

/* Design notes */
.note {
  background: var(--red-soft);
  border-radius: 8px; padding: 13px 16px; margin-bottom: 20px;
  font-size: 13.5px; color: var(--muted);
}
.note b { color: var(--ink); font-weight: 500; }


[hidden] { display: none !important; }

@media (max-width: 1000px) {
  .sidebar { transform: translateX(-100%); }
  .main { margin-left: 0; }
  .file-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: no-preference) {
  a, button, tr, .btn { transition: background-color .13s ease, color .13s ease; }
}

/* --- server-rendered additions ---------------------------------------------- */
.auth-wrap { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.auth-card { width: 100%; max-width: 380px; background: var(--card);
  border-radius: var(--r); box-shadow: var(--elev); padding: 32px; }
.auth-card h1 { margin: 0 0 4px; font-size: 22px; font-weight: 600; }
.auth-card p.sub { margin: 0 0 24px; color: var(--muted-2); font-size: 14px; }
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.field label { font-size: 12.5px; color: var(--muted); }
.field input { background: var(--bg); border: 0; border-radius: 9px; padding: 12px 14px;
  color: var(--ink); font-family: inherit; font-size: 15px; }
.field input:focus { outline: none; box-shadow: 0 0 0 2px rgba(var(--red-rgb),.6); }
.form-error { background: var(--red-soft); color: var(--red-ink); border-radius: 9px;
  padding: 11px 14px; font-size: 13.5px; margin-bottom: 18px; }
.w-full { width: 100%; justify-content: center; }
.empty { padding: 34px 20px; text-align: center; color: var(--muted-2); font-size: 14px; }
tbody tr a.rowlink { color: inherit; text-decoration: none; display: block; }

/* --- dashboard grids --------------------------------------------------------
   Cards stretch to the tallest in their row so the bottoms line up, and their
   bodies flex so content spreads rather than leaving a gap underneath. */
.dash-2, .dash-3 { display: grid; gap: 16px; margin-bottom: 16px; align-items: stretch; }
.dash-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.dash-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.dash-2 > .card, .dash-3 > .card {
  margin-bottom: 0; height: 100%; display: flex; flex-direction: column;
}
.dash-2 > .card > .bd, .dash-3 > .card > .bd { flex: 1; }
.dash-2 > .card > .tbl-wrap, .dash-3 > .card > .tbl-wrap { flex: 1; }

/* Stat rows inside a dashboard card spread over the available height. */
.spread { display: flex; flex-direction: column; justify-content: space-between; gap: 12px; }

/* Cards size to their content; tops align, bottoms are ragged. Use where the
   cards in a row hold lists of very different lengths. */
.dash-2.snug, .dash-3.snug { align-items: start; }
.dash-2.snug > .card, .dash-3.snug > .card { height: auto; }

/* A bar list: rows stacked tight at the top, never spread. */
.barlist { display: flex; flex-direction: column; gap: 11px; }

@media (max-width: 1100px) {
  .dash-2, .dash-3 { grid-template-columns: minmax(0, 1fr); }
}

/* --- footer ------------------------------------------------------------------
   Sits at the end of the content column, not pinned, so it never covers a table
   on a short page and never floats over a long one. */
.site-foot {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  margin-top: 34px; padding-top: 18px;
  border-top: 1px solid var(--line-2);
  font-size: 12.5px; color: var(--muted-2);
}
.site-foot a { color: var(--muted-2); text-decoration: none; }
.site-foot a:hover { color: var(--ink); text-decoration: underline; }
.site-foot .grow { flex: 1; }
@media (max-width: 1100px) { .site-foot .hide-sm { display: none; } }

/* Long-form prose: terms, notices, anything a person has to read. */
.prose { max-width: 68ch; }
.prose h2 { font-size: 17px; font-weight: 600; margin: 26px 0 8px; }
.prose p { margin: 0 0 13px; font-size: 14.5px; color: var(--muted); line-height: 1.62; }
.prose ul { margin: 0 0 13px; padding-left: 20px; color: var(--muted); font-size: 14.5px; line-height: 1.62; }
.prose li { margin-bottom: 5px; }

/* =============================================================================
   Loading states
   ============================================================================= */

/* --- skeleton ----------------------------------------------------------------
   A sheen that travels left to right across a dim block. The blocks are laid
   out to match the page being navigated to, so the swap to real content is a
   fill rather than a jump. */
@keyframes sk-sheen { 100% { background-position: 200% 0; } }

.sk {
  display: block;
  height: 13px;
  border-radius: 6px;
  background: linear-gradient(
    90deg,
    rgba(var(--scrim-rgb), .045) 0%,
    rgba(var(--scrim-rgb), .10) 42%,
    rgba(var(--scrim-rgb), .045) 84%
  );
  background-size: 200% 100%;
  animation: sk-sheen 1.4s ease-in-out infinite;
}

.sk-page { display: flex; flex-direction: column; gap: 16px; }

.sk-card {
  background: var(--card);
  border-radius: var(--r);
  box-shadow: var(--elev);
  padding: 20px;
  display: flex; flex-direction: column; gap: 12px;
}

.sk-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; }
.sk-2     { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 16px; }
.sk-file  { display: grid; grid-template-columns: minmax(0,1.12fr) minmax(300px,.88fr); gap: 20px; align-items: stretch; }
.sk-file > div { display: flex; flex-direction: column; gap: 16px; }
@media (max-width: 1000px) { .sk-2, .sk-file { grid-template-columns: minmax(0, 1fr); } }

.sk-table .sk-row {
  display: flex; align-items: center; gap: 14px;
  padding: 7px 0;
}
.sk-table .sk-row .sk { height: 11px; }

/* Cover the real content rather than replacing it: the page underneath stays
   in the DOM, so a cancelled navigation leaves nothing to rebuild. */
body.is-loading .main > *:not(#sk-overlay) { opacity: .12; pointer-events: none; }
#sk-overlay { animation: sk-in .18s ease-out; }
@keyframes sk-in { from { opacity: 0; } to { opacity: 1; } }

/* --- spinner -----------------------------------------------------------------
   Brand red arc on a faint track. Sized in em so it inherits whatever it sits
   in — a button, a heading, an overlay. */
@keyframes spin { to { transform: rotate(360deg); } }

.spinner {
  display: inline-block;
  width: 1.05em; height: 1.05em;
  border: 2px solid rgba(var(--scrim-rgb), .16);
  border-top-color: var(--red);
  border-radius: 50%;
  animation: spin .62s linear infinite;
  vertical-align: -2px;
}
.spinner.on-red { border-color: rgba(255,255,255,.35); border-top-color: #fff; }
.spinner.lg { width: 34px; height: 34px; border-width: 3px; }

/* The loop over a dimmed page while the next one loads: viewport-centred,
   above everything, and never in the way of a click that lands before the
   page turns. The ring itself is the .spinner above, one size up. */
.sk-spin {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center;
  pointer-events: none;
}
.sk-spin .spinner { width: 44px; height: 44px; border-width: 4px; }
@media (prefers-reduced-motion: reduce) {
  /* Still shown, still says "loading"; it just does not turn. */
  .sk-spin .spinner { animation: none; border-top-color: var(--red); }
}

/* A button that has been pressed and is waiting. The label is kept in the flow
   (hidden, not removed) so the button cannot change width mid-submit. */
.is-busy { position: relative; color: transparent !important; cursor: progress; }
.is-busy > * { visibility: hidden; }
.is-busy::after {
  content: "";
  position: absolute; top: 50%; left: 50%;
  width: 15px; height: 15px; margin: -7.5px 0 0 -7.5px;
  border: 2px solid rgba(var(--scrim-rgb), .25);
  border-top-color: var(--ink);
  border-radius: 50%;
  animation: spin .62s linear infinite;
}
button:not(.btn-red).is-busy::after { border-top-color: var(--red); }

/* Inline "working on it" for anywhere a spinner needs a label beside it. */
.working { display: inline-flex; align-items: center; gap: 9px; color: var(--muted); font-size: 13.5px; }

@media (prefers-reduced-motion: reduce) {
  .sk { animation: none; background: rgba(var(--scrim-rgb), .07); }
  .spinner, .is-busy::after { animation-duration: 1.6s; }
  #sk-overlay { animation: none; }
}

/* --- modal -------------------------------------------------------------------
   Native <dialog>: focus trapping, Esc to close and inertness of the page
   behind come for free, which is most of what makes a hand-rolled modal wrong. */
dialog.modal {
  width: min(680px, calc(100vw - 40px));
  padding: 0;
  border: 0;
  border-radius: var(--r);
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--elev);
}
dialog.modal::backdrop { background: rgba(0,0,0,.6); backdrop-filter: blur(2px); }

.modal-hd {
  display: flex; align-items: center; gap: 12px;
  padding: 17px 20px 13px;
}
.modal-hd h2 { margin: 0; font-size: 17px; font-weight: 500; flex: 1; }
.modal-bd { padding: 4px 22px 10px; }
/* A ticket is a description of a problem, not a one-liner. The default
   textarea height makes people write as if the box is the limit. */
.modal-bd textarea { min-height: 170px; }
.modal-ft {
  display: flex; justify-content: flex-end; gap: 9px;
  padding: 14px 20px 18px;
}

dialog.modal[open] { animation: modal-in .16s ease-out; }
@keyframes modal-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { dialog.modal[open] { animation: none; } }

/* --- triage ------------------------------------------------------------------
   Priority earns colour because it is the one thing you scan a queue for.
   Category is a quiet chip: useful once you are already looking at the row,
   never competing with the subject line for attention. */
.badge.pri-urgent { background: var(--red); }
.badge.pri-high   { background: var(--amber); color: #1a1a1a; }
.badge.pri-normal { background: var(--card-3); color: var(--muted); }
.badge.pri-low    { background: var(--card-2); color: var(--muted-2); }

.chip {
  display: inline-block; font-size: 12px; font-weight: 500;
  padding: 4px 11px; border-radius: 100px;
  background: var(--card-3); color: var(--muted); white-space: nowrap;
}

/* Two selects side by side, stacking on narrow screens rather than squashing. */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }

/* .field only ever styled inputs; the triage selects need to match them. */
.field select, .field textarea {
  background: var(--bg); border: 0; border-radius: 9px; padding: 12px 14px;
  color: var(--ink); font: inherit; font-size: 14px; width: 100%;
}
.field select:focus, .field textarea:focus {
  outline: none; box-shadow: 0 0 0 2px rgba(var(--red-rgb),.6);
}
.field select { appearance: none; cursor: pointer; padding-right: 34px;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 20px, calc(100% - 13px) 20px;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }

/* --- tabs ---------------------------------------------------------------------
   A segmented fill, not an underline: the theme is borderless, and a rule under
   one tab would be the only line on the page.

   The bar is hidden by default and shown once script has upgraded the card, so
   a browser without JS gets two open panels under real headings rather than
   buttons that do nothing. */
.tabs { display: none; gap: 4px; padding: 12px 12px 0; }
.js .tabs { display: flex; }

.tab {
  flex: 1; padding: 9px 10px; border: 0; border-radius: 9px;
  background: transparent; color: var(--muted-2);
  font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: background .14s ease, color .14s ease;
}
.tab:hover { color: var(--muted); background: var(--card-2); }
.tab.on { background: var(--card-3); color: var(--ink); }
.tab:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(var(--red-rgb),.6); }
@media (prefers-reduced-motion: reduce) { .tab { transition: none; } }

.tabpanel { padding: 14px 20px 20px; }
.tabpanel[hidden] { display: none; }

/* The panel's own heading exists for the no-JS case; the tab is the label
   once the tabs are live. */
.panel-h { margin: 0 0 14px; font-size: 17px; font-weight: 500;
           display: flex; align-items: center; gap: 12px; }
.panel-h .hint { margin-left: auto; font-size: 12.5px; color: var(--muted-2); font-weight: 400; }
.js .panel-h { display: none; }

/* --- the picker rows ----------------------------------------------------------
   Lifted out of inline styles so the two panels stay in step. */
.stack-8  { display: flex; flex-direction: column; gap: 8px; }
.stack-10 { display: flex; flex-direction: column; gap: 10px; }

.pick { display: flex; gap: 11px; align-items: flex-start; background: var(--bg);
        padding: 12px 14px; border-radius: 10px; cursor: pointer; }
.pick input { margin-top: 3px; }
.pick-t { flex: 1; }
.pick-t b { font-size: 14.5px; font-weight: 500; display: block; }
.pick-t span { font-size: 12.5px; color: var(--muted-2); }
.pick-p { font-size: 15px; }

.opt { display: flex; gap: 10px; align-items: center; font-size: 14px; }
.opt span { flex: 1; }
.opt-p { font-size: 12.5px; color: var(--muted-2); }

.terms { display: flex; gap: 10px; align-items: flex-start; font-size: 13.5px; color: var(--muted); }
.terms input { margin-top: 3px; }
.terms a { color: var(--red-ink); }
.foot-note { margin: 12px 0 0; font-size: 12.5px; color: var(--muted-2); text-align: center; }

/* The running total in step 2. The figure is the point of the line, so it is
   the size of the figure and the words around it stay out of its way. */
.sending {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 9px;
  /* Hard right, so it sits directly over the Next button in .step-actions
     below -- which is flex-end too. The two read as one block that way. */
  justify-content: flex-end; text-align: right;
  margin: 0 0 6px; font-size: 13px; color: var(--muted-2);
}
.sending b {
  color: var(--ink); font-weight: 500;
  font-family: "Roboto Mono", monospace; font-size: 23px; letter-spacing: -.01em;
}

/* --- upload wizard ------------------------------------------------------------
   The step bar is the tab component with numbers bolted on, so there is one
   implementation rather than two that drift apart. */
.wizard > .tabpanel { padding: 0; }

.steps { max-width: 540px; margin-bottom: 20px; padding: 0; }
.steps .tab { display: flex; align-items: center; justify-content: center; gap: 9px;
              padding: 11px 14px; font-size: 13.5px; }
.steps .tab .n {
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--card-3); color: var(--muted);
  font-family: "Roboto Mono", monospace; font-size: 11px;
  display: grid; place-items: center; flex: none;
}
.steps .tab.on .n   { background: var(--red); color: #fff; }
.steps .tab.done .n { background: var(--green); color: #fff; }

/* Next and Back only mean something once script is driving the steps. */
.step-actions { display: none; justify-content: flex-end; gap: 10px; margin: 4px 0 8px; }
.js .step-actions { display: flex; }

/* And the other way round: with no script the wizard is one long page, so it
   needs a Send of its own -- the one in .step-actions is hidden above. */
.nojs-send { display: flex; justify-content: flex-end; margin: 10px 0 8px; }
.js .nojs-send { display: none; }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 620px) { .grid-2 { grid-template-columns: 1fr; } }


/* --- the drop zone ------------------------------------------------------------
   A label wrapping a real file input: clicking anywhere opens the picker, and
   the input still submits normally with script off. */
/* A target, not a form field: centred, generous, and unmistakably changed
   when a file is over it or chosen. The quiet inset ring stands in for the
   dashed border the theme does not want. */
.drop {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 13px; text-align: center; color: var(--muted-2); font-size: 13.5px;
  background: var(--bg); border-radius: 12px; padding: 30px 22px;
  cursor: pointer;
  box-shadow: inset 0 0 0 1.5px var(--line);
  transition: background .14s ease, box-shadow .14s ease;
}
.drop:hover {
  background: var(--card-2);
  box-shadow: inset 0 0 0 1.5px var(--muted-2);
}
/* State is a fill, never a coloured edge. The ring stays exactly as it is --
   it is the edge of the target, which does not change just because there is
   now a file in it. */
.drop.over { background: rgba(var(--red-rgb), .22); }
.drop.has  { background: var(--red-soft); }
/* A refused pick: same fill-not-edge rule, said in the zone's own words. */
.drop.refused { background: rgba(var(--red-rgb), .10); color: var(--ink); }
.drop input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0;
                           overflow: hidden; clip: rect(0 0 0 0); }
.drop:focus-within { outline: none; box-shadow: 0 0 0 2px rgba(var(--red-rgb),.6); }
.drop-icon {
  width: 52px; height: 52px; flex: none; border-radius: 15px;
  background: var(--card-3); color: var(--muted);
  display: grid; place-items: center;
  transition: background .14s ease, color .14s ease, transform .14s ease;
}
.drop:hover .drop-icon { color: var(--ink); }
.drop.over  .drop-icon { background: var(--red); color: #fff; transform: translateY(-2px); }
.drop.has   .drop-icon { background: var(--red); color: #fff; }
.drop-icon svg { width: 25px; height: 25px; }

.drop-t { display: flex; flex-direction: column; gap: 4px; min-width: 0; max-width: 100%; }
.drop-t b {
  font-size: 14.5px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.drop-t span { font-size: 12.5px; color: var(--muted-2); line-height: 1.5; }
@media (prefers-reduced-motion: reduce) { .drop, .drop-icon { transition: none; } }
@media (prefers-reduced-motion: reduce) { .drop { transition: none; } }

/* --- what was asked for ------------------------------------------------------- */
.asked { margin-top: 18px; display: flex; flex-direction: column; gap: 8px; }
.asked .eyebrow { display: block; }
.asked .eyebrow + * { margin-top: -2px; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.note {
  margin: 0; padding: 11px 13px; border-radius: 10px;
  background: var(--bg); color: var(--muted);
  font-size: 13.5px; line-height: 1.55; white-space: pre-wrap;
}

/* --- availability matrix -------------------------------------------------------
   Sub-options down, solutions across. Header cells carry long solution names,
   so they wrap inside a narrow column instead of stretching the table. */
table.matrix th.mx {
  width: 92px; white-space: normal; vertical-align: bottom;
  font-size: 11.5px; line-height: 1.3; padding-bottom: 10px;
}
table.matrix td.mx { text-align: center; }
/* .tick itself is defined once, at the foot of this file. */
table.matrix tbody tr:hover td { background: var(--card-2); }
table.matrix input[type="checkbox"] { cursor: pointer; }


/* --- a solution and its sub-options --------------------------------------------
   One block per solution: the row you pick, and the extras that belong to it
   directly underneath. Borderless, so the grouping is carried by the shared
   fill rather than a rule around it. */

/* Indented to sit under the label text, not the radio, so the nesting reads. */
.subs { padding: 2px 16px 15px 41px; }
.subs .eyebrow { display: block; margin-bottom: 9px; }
.subs[hidden] { display: none; }
.subs-none { margin: 0; font-size: 13px; color: var(--muted-2); line-height: 1.5; }

/* --- price list ---------------------------------------------------------------
   Same shape as the order form: the solution, then what can be added to it,
   folded away until you ask for it. */
.price-card { margin-bottom: 12px; }
.price-card summary {
  list-style: none;               /* the default triangle, in both dialects */
  cursor: pointer;
}
.price-card summary::-webkit-details-marker { display: none; }
.price-card summary:focus-visible {
  outline: none; box-shadow: inset 0 0 0 2px rgba(var(--red-rgb),.6);
  border-radius: var(--r);
}

.price-head {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 17px 20px 15px;
}
.price-head.plain { opacity: .82; }          /* nothing to open */
.price-head .pick-t { flex: 1; min-width: 0; }
.price-head .price-tags { display: flex; gap: 5px; flex: none; padding-top: 2px; }
.price-head .price-amt { font-size: 17px; flex: none; display: inline-flex; align-items: center;
                         gap: 10px; white-space: nowrap; }
.price-head .chev {
  width: 18px; height: 18px; flex: none; color: var(--muted-2);
  margin-top: 3px; display: inline-flex; align-items: center; justify-content: center;
  transition: transform .16s ease, color .16s ease;
}
.price-head .chev svg { width: 18px; height: 18px; display: block; }
.price-card summary:hover .chev { color: var(--ink); }
.price-card details[open] .chev,
.price-card details[open] summary .chev { transform: rotate(180deg); }
.price-card details[open] summary { padding-bottom: 0; }
.price-card details[open] .price-head { padding-bottom: 6px; }
@media (prefers-reduced-motion: reduce) { .price-head .chev { transition: none; } }

/* The open panel lines up under the solution name, not the card edge. */
.price-card .subs { padding: 4px 20px 18px; }

/* The extras that can go with a solution, as chips on one line that wraps:
   a stacked list made every open row a column of near-identical lines. Each
   chip is name then price; "free" stays quiet, a surcharge is the figure. */
.price-subs { list-style: none; margin: 0; padding: 0;
              display: flex; flex-wrap: wrap; gap: 6px 8px; }
.price-subs li { display: inline-flex; align-items: baseline; gap: 7px;
                 padding: 5px 11px; border-radius: 999px; background: var(--fill);
                 font-size: 13px; line-height: 1.3; }
.price-subs li .n { min-width: 0; }
.price-subs li .v { font-size: 12.5px; color: var(--muted); white-space: nowrap; }
.price-subs li .free { font-family: "Plus Jakarta Sans", sans-serif;
                       font-size: 12px; color: var(--muted-2); }

/* --- theme toggle --------------------------------------------------------------
   One button, three states. The icon for the current mode is the only one
   shown; the others stay in the markup so switching needs no DOM building. */
.theme-btn svg { display: none; }
.theme-btn[data-mode="auto"]  .i-auto,
.theme-btn[data-mode="light"] .i-light,
.theme-btn[data-mode="dark"]  .i-dark { display: block; }

/* --- sign in -------------------------------------------------------------------
   Two panels in one card: what the workshop is doing on the left, the form on
   the right. The left side earns its space by being live -- the pill and the
   countdown are the real shop hours, not an illustration. */
.auth-split {
  width: 100%; max-width: 880px;
  display: grid; grid-template-columns: 1.04fr .96fr;
  background: var(--card); border-radius: 16px; box-shadow: var(--elev);
  overflow: hidden;
}
@media (max-width: 820px) {
  .auth-split { grid-template-columns: 1fr; max-width: 430px; }
}

.auth-brand {
  position: relative; overflow: hidden;
  padding: 38px 34px 34px;
  background:
    linear-gradient(158deg, rgba(var(--red-rgb), .17) 0%, rgba(var(--red-rgb), .02) 46%, transparent 72%),
    var(--bg);
}
.auth-watermark {
  position: absolute; right: -58px; bottom: -64px;
  width: 268px; height: 268px; color: var(--red); opacity: .075;
  pointer-events: none;
}
.auth-brand-body { position: relative; }

.auth-logo { display: block; height: 42px; width: auto; }

.auth-brand h2 {
  margin: 20px 0 8px; font-size: 21px; font-weight: 500;
  letter-spacing: -.2px; text-wrap: balance;
}
.auth-tag { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.55; max-width: 30ch; }

.auth-status { display: flex; align-items: center; gap: 9px; margin: 22px 0 0; flex-wrap: wrap; }
.auth-status .lbl { color: var(--muted-2); font-size: 13px; }

.auth-points {
  list-style: none; margin: 24px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 11px;
}
.auth-points li {
  position: relative; padding-left: 20px;
  font-size: 13px; line-height: 1.55; color: var(--muted);
}
.auth-points li::before {
  content: ""; position: absolute; left: 0; top: .48em;
  width: 8px; height: 8px; border-radius: 2px;
  background: var(--red); opacity: .85;
  transform: rotate(45deg);
}

.auth-form { padding: 38px 34px 34px; display: flex; flex-direction: column; }
.auth-form-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 24px; }
.auth-form-head > div { flex: 1; min-width: 0; }
.auth-form h1 { margin: 0 0 4px; font-size: 22px; font-weight: 500; letter-spacing: -.2px; }
.auth-form .sub { margin: 0; color: var(--muted-2); font-size: 13.5px; }

.auth-foot {
  margin: 20px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--muted-2);
}
.auth-foot a { color: var(--red-ink); }

/* --- section tabs --------------------------------------------------------------
   The same segmented look as the in-card tabs, but these are links to real
   pages, so they are always visible -- there is no script to wait for. */
.navtabs {
  display: inline-flex; gap: 4px; padding: 4px;
  background: var(--bg); border-radius: 11px; margin-bottom: 18px;
}
.navtabs a {
  padding: 8px 15px; border-radius: 8px; text-decoration: none;
  color: var(--muted-2); font-size: 13.5px; font-weight: 500;
  white-space: nowrap; transition: background .14s ease, color .14s ease;
}
.navtabs a:hover { color: var(--muted); background: var(--fill); }
.navtabs a[aria-current="page"] { background: var(--card-3); color: var(--ink); }
.navtabs a:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(var(--red-rgb), .6); }
@media (prefers-reduced-motion: reduce) { .navtabs a { transition: none; } }

/* --- notices -------------------------------------------------------------------
   Type carries meaning, so it gets a colour and an icon rather than a label:
   info, success, warning. The tint is the colour at low alpha so it reads on
   either theme without a second palette. */
.notices { display: flex; flex-direction: column; gap: 12px; }

.notice {
  display: flex; gap: 13px;
  padding: 14px 16px; border-radius: 11px;
  background: var(--bg);
}
.notice-i {
  flex: none; width: 30px; height: 30px; border-radius: 9px;
  display: grid; place-items: center;
}
.notice-i svg { width: 16px; height: 16px; }
.notice-b { min-width: 0; flex: 1; }
.notice-b b { display: block; font-size: 14px; font-weight: 500; margin-bottom: 3px; }
.notice-b p { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--muted); white-space: pre-wrap; }
.notice-b time { display: block; margin-top: 7px; font-size: 12px; color: var(--muted-2); }

.notice.n-info    .notice-i { background: rgba(59,157,255,.16);  color: var(--blue-ink); }
.notice.n-success .notice-i { background: rgba(40,167,69,.16);   color: var(--green-ink); }
.notice.n-warning .notice-i { background: rgba(255,159,10,.18);  color: var(--amber-ink); }
.notice.n-warning { background: rgba(255,159,10,.07); }

/* --- per-dealer feature exceptions --------------------------------------------- */
.ovr { display: flex; align-items: center; gap: 14px; margin: 0; }
.ovr-n { flex: 1; min-width: 0; font-size: 13.5px; }
.ovr-n span { display: block; font-size: 12px; color: var(--muted-2); margin-top: 1px; }
.ovr .seg button { padding: 5px 11px; font-size: 12.5px; }

/* --- dashboard split ----------------------------------------------------------
   Recent requests get the width -- they are a table -- and the notices column
   takes what is left. Stacks before either gets too narrow to read. */
.dash-split {
  display: grid; gap: 16px; align-items: stretch;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  margin-bottom: 16px;
}
.dash-split > div > .card:last-child { margin-bottom: 0; }
@media (max-width: 1080px) { .dash-split { grid-template-columns: 1fr; } }

/* --- the read card -------------------------------------------------------------
   The file on the left with the licence under it, what was read on the right.
   Both columns stretch to the taller of the two, and the drop zone grows to
   take up whatever the right-hand fields leave. */
.read-grid {
  display: grid; gap: 18px; align-items: stretch;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
@media (max-width: 900px) { .read-grid { grid-template-columns: 1fr; } }

.read-left { display: flex; flex-direction: column; gap: 14px; }
.read-left .drop { flex: 1; }
/* The two columns are the same height by the grid; the question is who fills
   it. On the left the drop zone stretches. On the right nothing did, so when
   the left came out taller -- drop zone plus two chip rows against four
   selects, 16px on the first card -- the last select sat short of the foot.
   space-between hands that difference to the gaps, a few px each, and does
   nothing at all when the right is the taller side. The house rule at the
   foot of this file: if content makes one taller, they all get taller. */
.read-right { display: flex; flex-direction: column; gap: 14px; justify-content: space-between; }
.read-right .field { margin-bottom: 0; }

.radioset { border: 0; margin: 0; padding: 0; }
.radioset legend { padding: 0 0 7px; font-size: 12.5px; color: var(--muted); }
.radiorow { display: flex; gap: 8px; }
.radiochip {
  position: relative;                /* the input is hidden against this */
  flex: 1; display: flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--bg); border-radius: 9px; padding: 11px 14px;
  font-size: 14px; cursor: pointer;
  transition: background .13s ease, color .13s ease;
}

/* The radio is hidden, not removed: it still posts the value, still takes
   arrow keys and still works with no script -- the same treatment the top-up
   bundles get. The chip itself is the indicator. */
.radiochip input {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}

/* Chosen reads as chosen: the whole chip, not a line around it. No script
   involved -- the same way the top-up bundles do it. */
.radiochip:has(input:checked) {
  background: var(--red); color: #fff; font-weight: 500;
}
.radiochip:hover:not(:has(input:checked)) { background: var(--fill-2); }
.radiochip:focus-within { box-shadow: 0 0 0 2px rgba(var(--red-rgb), .6); }

/* --- top-up bundles ------------------------------------------------------------
   Selectable cards, with one highlight that travels to whichever is chosen.
   The radio is visually hidden rather than removed: it still posts the value,
   still takes arrow keys, and still works with no script at all. */
.bundles {
  position: relative;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); gap: 12px;
}

/* The grid stretches the cell; the cell has to pass that height to the face,
   or the card you can see stops short of the card the grid laid out. This is
   the rule for any wrapper around a visible surface -- see .uniform-row. */
.bundle { position: relative; z-index: 1; cursor: pointer; display: flex; }
.bundle input {
  position: absolute; width: 1px; height: 1px; opacity: 0;
  margin: 0; pointer-events: none;
}
.bundle-face {
  flex: 1;
  display: flex; flex-direction: column; gap: 3px;
  padding: 17px 18px; border-radius: 12px;
  background: var(--bg);
  transition: background .2s ease, color .2s ease, box-shadow .2s ease;
}
.bundle:hover .bundle-face { background: var(--card-2); }
.bundle input:focus-visible + .bundle-face { box-shadow: 0 0 0 2px rgba(var(--red-rgb), .6); }

.bundle-amt   { font-size: 24px; font-weight: 500; line-height: 1.1; }
.bundle-name  { font-size: 13.5px; }
.bundle-bonus { font-size: 12.5px; color: var(--green-ink); margin-top: 2px; }
.bundle-bonus.muted { color: var(--muted-2); }

/* No script: the card paints itself, so a selection is always visible. */
.bundle:has(input:checked) .bundle-face { background: var(--red); color: #fff; }
.bundle:has(input:checked) .bundle-bonus { color: rgba(255,255,255,.85); }

/* With script the travelling block does the colour, so the card goes clear
   and the one underneath shows through as it arrives. */
.bundle-slide { display: none; }
.js .bundles[data-ready] .bundle-slide {
  display: block;
  position: absolute; top: 0; left: 0;
  border-radius: 12px; background: var(--red);
  transform: translate3d(0, 0, 0);
  transition: transform .32s cubic-bezier(.33, .9, .3, 1),
              width .32s cubic-bezier(.33, .9, .3, 1),
              height .32s cubic-bezier(.33, .9, .3, 1),
              opacity .18s ease;
  z-index: 0; opacity: 0; pointer-events: none;
}
.js .bundles[data-ready] .bundle:has(input:checked) .bundle-face { background: transparent; }

@media (prefers-reduced-motion: reduce) {
  .js .bundles[data-ready] .bundle-slide { transition: opacity .18s ease; }
  .bundle-face { transition: none; }
}

/* Peer columns of cards finish level: the column stretches and its last card
   takes up whatever slack is left. Same rule as .dash-2 / .dash-3 already use. */
.file-grid > div,
.dash-split > div,
.read-grid > div { display: flex; flex-direction: column; }
.file-grid  > div > .card:last-child { flex: 1; margin-bottom: 0; }

/* Except where the last card is the small one. On a request page the money
   card is a total and a button -- stretching it strands the button in the
   middle of an empty card. It keeps its own height at the foot of the column
   and the card above it takes the slack instead, which finishes the two
   columns level either way. */
.file-grid > div.side-anchor > .card:last-child  { flex: 0 0 auto; }
.file-grid > div.side-anchor > .card:first-child { flex: 1; }
.dash-split > div > .card:last-child { flex: 1; margin-bottom: 0; }

/* Colour says what is chosen; an edge never does.
     A control that is picked, ticked or holding a file is filled -- the
     licence chips, the transmission chips, the job list and its extras, the
     top-up bundles, the drop zones. A border is structure (the edge of a
     target, a panel, a hairline between rows) or it is focus. It is never
     "this one is selected": that was three different colours of ring saying
     what a fill says better.

   The house rule this file is kept to:
     1. Items on a row are the same height. Grid and flex stretch them by
        default -- the trap is a wrapper that stretches while the surface
        inside it does not, which is what .bundle and these columns fix.
     2. Controls of the same kind in a group are the same size: every chip in
        a row, every tab in a strip, every card in a set.
     3. If content makes one taller, they all get taller; never let one shrink.
   Centring inside a single control (an icon beside its label) is not this --
   that is alignment within one thing, not sizing across several. */

/* --- ledger kinds --------------------------------------------------------------
   Money in and money out read differently at a glance; the rest stay quiet. */
.kind {
  display: inline-block; font-size: 11.5px; font-weight: 500;
  padding: 3px 9px; border-radius: 6px; white-space: nowrap;
  background: var(--card-3); color: var(--muted);
}
.kind.k-topup,
.kind.k-bundle_bonus { background: rgba(40,167,69,.16);  color: var(--green-ink); }
.kind.k-refund       { background: rgba(59,157,255,.16); color: var(--blue-ink); }
.kind.k-file_charge  { background: var(--fill-2); color: var(--muted); }

.pay-row { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.pay-row .btn-red { flex: none; }
.after-note { margin: -4px 0 20px; font-size: 13px; color: var(--muted-2); }
.after-note a { color: var(--red-ink); }

/* --- list filters and paging ---------------------------------------------------
   A GET form, so the filtered view is a URL: bookmarkable, shareable, and
   survives the back button. Controls sit on one row and match each other's
   height, per the house rule at the foot of this file. */
.filters {
  display: flex; gap: 10px; align-items: stretch; flex-wrap: wrap;
  margin-bottom: 16px;
}
.filters .field { margin: 0; }
.filters .field.search { flex: 1; min-width: 240px; position: relative; }
.filters .field.search svg {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--muted-2); pointer-events: none;
}
.filters .field.search input { padding-left: 38px; width: 100%; }
.filters select, .filters input, .filters button, .filters .btn {
  height: 42px;                      /* every control on this row, one height */
}
.filters select { min-width: 150px; }
.filters .btn.clear {
  display: inline-flex; align-items: center; text-decoration: none;
  padding: 0 16px; color: var(--muted);
}

.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;
}

.pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-top: 14px;
}
.pager-count { font-size: 13px; color: var(--muted-2); }
.pager-links { display: flex; align-items: center; gap: 8px; }
.pg {
  display: inline-flex; align-items: center; height: 36px; padding: 0 14px;
  border-radius: 9px; background: var(--fill); color: var(--muted);
  text-decoration: none; font-size: 13.5px;
}
.pg:hover { background: var(--fill-2); color: var(--ink); }
.pg.off { opacity: .4; pointer-events: none; }
.pg-now { font-size: 13px; color: var(--muted-2); padding: 0 4px; }

/* --- the summary step ----------------------------------------------------------
   A review, not a second form: everything here is read back from what was
   entered, so there is nothing to disagree with. */
.sum-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: start; }
@media (max-width: 760px) { .sum-grid { grid-template-columns: 1fr; } }
.sum-col .eyebrow { display: block; margin-bottom: 10px; }
.sum-col .spec { margin: 0; }

.sum-lines { list-style: none; margin: 0 0 4px; padding: 0;
             display: flex; flex-direction: column; gap: 9px; }
.sum-lines li { display: flex; align-items: baseline; gap: 14px; font-size: 14px; }
.sum-lines li .n { flex: 1; min-width: 0; }
.sum-lines li .n span { display: block; font-size: 12.5px; color: var(--muted-2); margin-top: 1px; }
.sum-lines li .v { font-family: "Roboto Mono", monospace; font-size: 13.5px; white-space: nowrap; }
.sum-lines li.free .v { font-family: inherit; font-size: 12.5px; color: var(--muted-2); }

.sum-total {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: 16px; padding-top: 14px;
  box-shadow: inset 0 1px 0 var(--line);
  font-size: 15px; font-weight: 500;
}
.sum-total .mono { font-size: 21px; }
.sum-note { margin: 12px 0 0; font-size: 12.5px; color: var(--muted-2); line-height: 1.55; }
.sum-eta { margin: 0; font-size: 14.5px; line-height: 1.55; }
.sum-empty { color: var(--muted-2); font-size: 13.5px; }

/* --- ticket attachments --------------------------------------------------------
   The picker is a label around a hidden input: stylable, still a real control,
   and still usable with no script. House rule at the foot of this file -- the
   picker matches the height of the buttons it sits above. */
.file-pick {
  display: flex; align-items: center; gap: 10px;
  min-height: 42px; margin-top: 12px; padding: 0 14px;
  border: 1px dashed var(--line-2); border-radius: 10px;
  background: var(--fill); cursor: pointer;
  font-size: 13.5px; color: var(--muted);
}
.file-pick:hover { border-color: rgba(var(--red-rgb), .5); color: var(--ink); }
.file-pick input[type=file] { flex: 1; min-width: 0; font-size: 13px; color: inherit; }
.file-pick svg { width: 17px; height: 17px; flex: none; color: var(--muted-2); }
.file-pick .pick-hint { color: var(--muted-2); font-size: 12.5px; }
/* Same rule here: a file in it is a fill, not a border that changes colour.
   The dashed edge says "drop something here" and stops saying it once
   something is there. */
.file-pick.has-file {
  border-color: transparent; background: var(--red-soft); color: var(--ink);
}
.file-pick.has-file svg { color: var(--red-ink); }

/* With script, the native input is replaced by the label's own text. */
.js .file-pick input[type=file] {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.js .file-pick .pick-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The row under a composer: the cancel link, the send button and anything
   else in it are one height, like every other action row here. The flex rule
   it needs is already up with .composer itself -- this only sizes them. */
.composer .foot button,
.composer .foot .btn {
  display: inline-flex; align-items: center; justify-content: center;
  height: 42px; padding: 0 16px;
}

.att-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 10px; }
.att-chip {
  display: inline-flex; align-items: center; gap: 8px;
  max-width: 100%; height: 30px; padding: 0 11px;
  border-radius: 8px; background: var(--fill-2); color: var(--muted);
  text-decoration: none; font-size: 13px;
}
.att-chip:hover { background: var(--fill); color: var(--ink); }
.att-chip svg { width: 14px; height: 14px; flex: none; color: var(--muted-2); }
.att-chip .att-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.att-chip .att-size { font-size: 11.5px; color: var(--muted-2); }

/* --- avatars -------------------------------------------------------------------
   The picture fills the square; the initials stand in when there is none. Both
   sit in the same box so the sidebar does not shift when one is added. */
.ava img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }
.ava .ini {
  font-size: 14.5px; font-weight: 500; letter-spacing: .4px;
  color: var(--muted); font-family: inherit;
}
a.ava { text-decoration: none; }
a.ava:hover { background: var(--fill-2); }
a.ava:hover .ini { color: var(--ink); }

.ava-big { width: 72px; height: 72px; border-radius: 14px; }
.ava-big .ini { font-size: 24px; letter-spacing: .5px; }

/* One row, one height -- the house rule at the foot of this file. */
.ava-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.ava-row .ava { display: grid; place-items: center; background: var(--fill); flex: none; }
.ava-row form { display: flex; align-items: center; gap: 10px; margin: 0; }
.ava-row .file-pick { margin: 0; height: 42px; }
.ava-row button { height: 42px; }
.ava-pick { flex: 1; min-width: 260px; }
.ava-pick .file-pick { flex: 1; }

.pw-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
@media (max-width: 760px) { .pw-grid { grid-template-columns: 1fr; } }
.pw-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-top: 16px;
}
.pw-foot button { height: 42px; }

/* --- the header menus ----------------------------------------------------------
   A <details> dropdown: the browser handles open, close and focus, and every
   row inside is a form. The summary is sized exactly like the icon buttons
   beside it -- house rule at the foot of this file. */
.menu-wrap { position: relative; }
.menu-wrap > summary {
  display: inline-flex; align-items: center; gap: 6px;
  list-style: none; cursor: pointer;
}
.menu-wrap > summary::-webkit-details-marker { display: none; }
.menu-wrap > summary::marker { content: ""; }
/* The language button carries a flag and a code, so it is wider than the two
   square buttons beside it -- same height, which is what keeps the row even. */
.menu-wrap > summary { justify-content: center; }
.lang-menu > summary { width: auto; padding: 0 9px; gap: 7px; }
.menu-wrap > summary .code {
  font-size: 11.5px; font-weight: 600; letter-spacing: .04em;
}

/* The flag itself: a small rectangle with a hairline, so a white or pale flag
   still reads as an object against the header rather than bleeding into it. */
/* .icon-btn svg sizes every icon in the header to a 16px square, which is
   right for a glyph and wrong for a flag -- a flag has an aspect ratio. */
.menu-wrap > summary .flag { width: 18px; height: 13px; }
.flag {
  width: 18px; height: 13px; flex: none; border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(var(--scrim-rgb), .22);
  object-fit: cover;
}
/* The tick's rule sizes every svg in a menu row to a 15px square; a flag
   needs its own shape here too. */
.menu-wrap .menu-row .flag { width: 20px; height: 14px; }
.menu-row .menu-code {
  font-size: 11px; font-weight: 600; letter-spacing: .04em; color: var(--muted-2);
}
.menu-row.on .menu-code { color: rgba(var(--scrim-rgb), .55); }
.menu-wrap[open] > summary { background: var(--fill-2); color: var(--ink); }

.menu-wrap .menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 40;
  min-width: 206px; padding: 7px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: 0 18px 44px rgba(0, 0, 0, .28);
}
.menu-wrap .menu-h {
  display: block; padding: 6px 9px 7px; font-size: 10.5px; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase; color: var(--muted-2);
}
.menu-wrap .menu form { margin: 0; }
.menu-wrap .menu-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; height: 36px; padding: 0 9px;
  background: transparent; border-radius: 8px;
  font-size: 13.5px; color: var(--muted); text-align: left;
}
.menu-wrap .menu-row:hover { background: var(--fill); color: var(--ink); }
.menu-wrap .menu-row.on { color: var(--ink); font-weight: 500; }
.menu-wrap .menu-row svg { width: 15px; height: 15px; color: var(--red-ink); }
.menu-wrap .menu-note {
  display: block; padding: 8px 9px 5px; margin-top: 4px;
  box-shadow: inset 0 1px 0 var(--line);
  font-size: 11.5px; line-height: 1.45; color: var(--muted-2);
}

/* --- a row of chips that stays a grid ------------------------------------------
   .radiorow is flex, which is right for two chips. Five of them need to wrap,
   and a wrapped flex row leaves the last line stretched to a different width
   from the rest. A grid of equal columns keeps every chip the same size on
   every line, which is the house rule at the foot of this file. */
.radiogrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(126px, 1fr));
  gap: 8px;
}

.car-time { border: 0; padding: 0; margin: 18px 0 0; }
.car-time legend {
  padding: 0 0 9px; font-size: 12.5px; font-weight: 500; color: var(--muted);
}

/* How long the car is available, where staff read it. Not a status: it is a
   fact about the dealer's day, so it stays quiet until it is a short one. */
.ramp {
  display: inline-block; padding: 2px 8px; border-radius: 6px;
  background: rgba(var(--red-rgb), .13); color: var(--red-ink);
  font-size: 11.5px; font-weight: 500; white-space: nowrap;
}

/* --- the file conversation -----------------------------------------------------
   Same timeline as everywhere else; these only cover what the thread adds. */
.tl .tl-kind {
  font-size: 12px; color: var(--muted-2);
}
.tl .tl-quiet { color: var(--muted-2); font-style: italic; }

/* The tuned file, before it is paid for. The chip says what pressing it costs,
   because pressing it is what takes the money. */
button.att-chip {
  border: 0; font-family: inherit; font-size: 13px; cursor: pointer;
}
.att-chip.pay {
  background: var(--red); color: #fff;
}
.att-chip.pay:hover { background: #ff5147; color: #fff; }
.att-chip.pay svg { color: rgba(255,255,255,.8); }
.att-chip.pay .att-size { color: rgba(255,255,255,.9); font-weight: 500; }

/* --- a second read on one request ----------------------------------------------
   Set apart from the main spec list without becoming a card of its own: it is
   part of the same job, not a second one. */
.second-read {
  margin-top: 18px; padding-top: 15px;
  box-shadow: inset 0 1px 0 var(--line);
}
.second-read .eyebrow { display: block; margin-bottom: 10px; }
.second-read .spec { margin: 0; }

/* --- the extra read on the upload form ----------------------------------------
   Closed it is a placeholder; open it is the read card again, so everything
   inside the grid is styled by the same rules as the first one. */
.add-read {
  display: flex; align-items: center; gap: 14px; width: 100%;
  padding: 16px 18px; text-align: left;
  background: transparent; border: 1px dashed var(--line-2); border-radius: 12px;
  color: var(--muted); cursor: pointer;
}
.add-read:hover { border-color: rgba(var(--red-rgb), .5); color: var(--ink); background: var(--fill); }
.add-read .plus {
  width: 34px; height: 34px; border-radius: 9px; flex: none;
  background: var(--fill); display: grid; place-items: center; color: var(--red-ink);
}
.add-read .plus svg { width: 17px; height: 17px; }
.add-read .add-t { display: flex; flex-direction: column; gap: 3px; }
.add-read .add-t b { font-size: 14.5px; font-weight: 500; }
.add-read .add-t span { font-size: 12.5px; color: var(--muted-2); }

.extra-add .bd { padding: 16px 20px; }
.extra-read .same-ecu { margin-bottom: 0; }
.extra-read .same-ecu b { font-weight: 500; }
.extra-read .hd .hint { font-size: 12.5px; color: var(--muted-2); }
.extra-read .hd .drop-read { margin-top: 0; }

/* Greyed because it is copied from the read above, not because it is broken. */
.read-right.mirrored { opacity: .45; pointer-events: none; }

.drop-read {
  margin-top: 14px; height: 38px; padding: 0 14px;
  background: transparent; color: var(--muted-2); font-size: 13px;
}
.drop-read:hover { background: var(--fill); color: var(--ink); }

/* --- the job pickers -----------------------------------------------------------
   Step 2 printed the whole catalogue once per read: eight choices, four times,
   seven screens of scrolling. One picker is open at a time now, the rest fold
   to their header, and the choices sit two to a row. */
.picker { overflow: hidden; }

.picker-hd {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 16px 20px; background: transparent; border-radius: 0;
  text-align: left; cursor: pointer;
}
.picker-hd:hover { background: var(--fill); }
.picker-hd h2 { margin: 0; font-size: 17px; font-weight: 500; }
.picker-hd .hint { font-size: 12.5px; color: var(--muted-2); }

/* What was chosen, on the folded header. Quietened, not hidden. */
.picker-hd .picked {
  display: inline-flex; align-items: baseline; gap: 10px; min-width: 0;
}
.picker-hd .picked-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 34ch; font-size: 13.5px; color: var(--muted);
}
.picker-hd .picked-p { font-size: 13.5px; color: var(--ink); }
.picker.open .picker-hd .picked { display: none; }

.picker-hd .chev { display: grid; place-items: center; width: 20px; height: 20px; color: var(--muted-2); }
.picker-hd .chev svg { width: 17px; height: 17px; transition: transform .16s ease; }
.picker.open .picker-hd .chev svg { transform: rotate(180deg); }

/* Two to a row: the list is eight long and every row is one line. */
.sol-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(258px, 1fr)); gap: 8px;
}
.sol-grid .pick {
  align-items: center; gap: 10px; padding: 11px 13px; border-radius: 10px;
}
.sol-grid .pick .pick-t b { font-size: 14px; }
.sol-grid .pick .pick-t span { font-size: 11.5px; }
.sol-grid .pick-p { font-size: 13.5px; }

/* Chosen is a fill, not a line around it -- the same language the licence
   chips, the transmission chips and the top-up bundles already speak.

   And no little box beside it. The radio and the checkbox said the same thing
   the fill says, in the browser's own grey, which looked like a leftover on a
   red row. They are hidden rather than removed: each one still posts its
   value, still takes arrow keys, and still works with no script -- the same
   treatment .radiochip and .bundle already get. The focus ring stays, because
   that is the only thing left telling a keyboard where it is. */
.sol-grid .pick { position: relative; transition: background .13s ease, color .13s ease; }
.sol-grid .pick input,
.sub-grid .opt input {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.sol-grid .pick:focus-within,
.sub-grid .opt:focus-within { box-shadow: 0 0 0 2px rgba(var(--red-rgb), .6); }

.sol-grid .pick.on,
.sol-grid .pick:has(input:checked) {
  background: var(--red); color: #fff;
}
.sol-grid .pick.on .pick-t span,
.sol-grid .pick:has(input:checked) .pick-t span { color: rgba(255, 255, 255, .85); }
.sol-grid .pick.on .pick-p,
.sol-grid .pick:has(input:checked) .pick-p { color: #fff; }

/* And the extras under it, which are controls of the same kind. */
.sub-grid .opt {
  position: relative;
  padding: 9px 12px; border-radius: 9px; background: var(--fill);
  cursor: pointer; transition: background .13s ease, color .13s ease;
}
.sub-grid .opt:hover:not(:has(input:checked)) { background: var(--fill-2); }
.sub-grid .opt:has(input:checked) { background: var(--red); color: #fff; }
.sub-grid .opt:has(input:checked) .opt-p { color: rgba(255, 255, 255, .9); }

/* The extras for whichever job is picked, under the grid at full width --
   inside a column they would push one side taller than the other. */
.picker .subs { padding: 16px 2px 0; }
.picker .subs .eyebrow { display: block; margin-bottom: 10px; }
.sub-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(226px, 1fr)); gap: 9px 18px;
}

/* --- the bill, a line per read -------------------------------------------------
   A request can carry an ECU and a gearbox. Each is its own line with its own
   price and its own button, so paying for one never hands over the other. */
.bill { list-style: none; margin: 0 0 4px; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.bill-line {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center; gap: 12px;
}
.bill-line .bl-what { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.bill-line .bl-what b {
  font-size: 13.5px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bill-line .bl-what span { font-size: 11.5px; color: var(--muted-2); }
.bill-line .bl-p { font-size: 13.5px; white-space: nowrap; }
.bill-line .bl-do form { margin: 0; }
.bill-line .bl-do .btn,
.bill-line .bl-do button {
  display: inline-flex; align-items: center; justify-content: center;
  height: 32px; padding: 0 13px; font-size: 13px; text-decoration: none;
}
.bill-line .bl-do .hint { font-size: 12px; color: var(--muted-2); }
.bill-line.done .bl-p { color: var(--muted-2); }

.attach-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 4px; }
.attach-form select { height: 38px; max-width: 190px; }
.attach-form input[type=file] { flex: 1; min-width: 150px; font-size: 13px; color: var(--muted); }
.attach-form button { height: 38px; }

/* --- the catalogue's own tick boxes -------------------------------------------
   Same treatment as the dealer-facing ones: the fill is the state and the box
   goes. Still a real checkbox underneath -- it posts, it takes the keyboard,
   and it works with no script. */
.opt-chip {
  position: relative;
  padding: 9px 12px; border-radius: 9px; background: var(--fill);
  cursor: pointer; transition: background .13s ease, color .13s ease;
}
.opt-chip input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.opt-chip:hover:not(:has(input:checked)) { background: var(--fill-2); }
.opt-chip:has(input:checked) { background: var(--red); color: #fff; }
.opt-chip:focus-within { box-shadow: 0 0 0 2px rgba(var(--red-rgb), .6); }

/* --- a tick in a grid ----------------------------------------------------------
   The catalogue's option x solution matrix, the opening-hours "closed" column
   and the team's permission grid are all grids of one-bit answers, where a
   labelled chip does not fit. So the box IS the fill: faint when off, brand
   red with a tick drawn in it when on. The checkbox underneath is untouched --
   it posts, it takes the keyboard, and it works with no script. */
.tick {
  position: relative;
  display: inline-grid; place-items: center;
  width: 26px; height: 26px; border-radius: 8px;
  background: var(--fill); cursor: pointer;
  transition: background .12s ease;
}
.tick input {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.tick:hover:not(:has(input:checked)) { background: var(--fill-2); }
.tick:has(input:checked) { background: var(--red); }

/* The tick itself, drawn rather than bordered: two edges of a box, turned. */
.tick:has(input:checked)::after {
  content: "";
  width: 10px; height: 5px;
  border-left: 2px solid #fff; border-bottom: 2px solid #fff;
  transform: rotate(-45deg) translate(1px, -2px);
}
.tick:focus-within { box-shadow: 0 0 0 2px rgba(var(--red-rgb), .6); }


/* --- adding a read, from the request page --------------------------------------
   A modal, because the service picker needs the width to show the extras under
   each job -- in the side column it could only have been a bare <select>, and
   a dealer adding a gearbox read later could not have ordered a delete with it.

   It ships open in the markup and the script closes it, so with no script it
   is a form at the foot of the page. :modal matches only a dialog opened with
   showModal(), which is exactly the difference we need to style. */
dialog.modal-wide { width: min(880px, calc(100vw - 40px)); }
dialog.modal[open]:not(:modal) {
  position: static; width: 100%; max-width: none;
  margin: 0 0 20px; box-shadow: var(--elev);
}
dialog.modal[open]:not(:modal) [data-close] { display: none; }

.modal-hd .hint { font-size: 12.5px; color: var(--muted-2); }
.modal-bd .read-grid { margin-bottom: 18px; }
.modal-bd .picker { box-shadow: none; background: var(--bg); margin-bottom: 0; }
.modal-bd .picker .picker-hd { padding: 14px 16px; }
.modal-bd .picker > .bd { padding: 0 16px 16px; }
.modal-ft { flex-wrap: wrap; align-items: center; }

/* --- the reference pages -------------------------------------------------------
   Vehicle Specs is a table of what we have seen; the Library is a stack of
   notes that open where they sit. */
.ecu-list { display: flex; flex-wrap: wrap; gap: 5px; }
.ecu-list code {
  font-family: "Roboto Mono", monospace; font-size: 11.5px;
  background: var(--fill); border-radius: 6px; padding: 3px 7px; white-space: nowrap;
}
td.r .sub { display: block; font-size: 11px; color: var(--muted-2); }

.lib-note { margin-bottom: 12px; }
.lib-note > summary { padding: 15px 20px; }
.lib-note[open] > summary { padding-bottom: 8px; }
.lib-body {
  white-space: pre-wrap; line-height: 1.62; font-size: 14px;
  color: var(--muted); padding-top: 0;
}

/* --- the catalogue widget ------------------------------------------------------
   An iframe from specs.drvdnlabs.com, which is the only supported way in: its
   API session is bound to the viewer and there is no endpoint that returns the
   catalogue. We size the frame and otherwise keep our hands off -- the whole
   point of the iframe is that neither side's CSS reaches the other. */
.specs-embed { min-height: 120px; }
/* The loader sizes the frame from the widget's own height messages (91px
   at rest, taller once a vehicle is chosen); a min-height here only puts an
   empty box under it. */
.specs-embed iframe {
  width: 100%; border: 0; display: block;
  border-radius: 10px; background: var(--bg);
}

/* A data log is neither a read nor a tune: quieter than both, and marked so
   nobody opens it expecting a binary. */
.att-chip.log { background: var(--fill-2); }
.att-chip.log svg { color: var(--blue); }
.att-chip.log .att-size { font-style: italic; }

/* --- the library, by vehicle then control unit ---------------------------------
   Facets are links, not buttons, because a filtered library is a URL. A chosen
   one is filled: colour says what is chosen, an edge never does. */
.facet-card { margin-bottom: 18px; }
.facet-row { display: flex; gap: 12px; align-items: baseline; padding: 7px 0; }
.facet-row + .facet-row { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 11px; }
.facet-row .eyebrow { flex: 0 0 104px; padding-top: 4px; }
.facets { display: flex; flex-wrap: wrap; gap: 6px; }
.facet {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--fill); color: var(--muted);
  border: 0; border-radius: 8px; padding: 6px 11px;
  font-size: 13px; text-decoration: none; white-space: nowrap;
}
.facet:hover { background: var(--fill-2); color: var(--ink); }
.facet.on { background: var(--red); color: #fff; font-weight: 500; }
.facet:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(var(--red-rgb),.6); }
.facet-n {
  font-family: "Roboto Mono", monospace; font-size: 11px;
  color: var(--muted-2); background: var(--card); border-radius: 5px; padding: 1px 5px;
}
.facet.on .facet-n { color: #fff; background: rgba(0,0,0,.22); }

.lib-group { margin-bottom: 22px; }
.lib-make {
  margin: 0 0 2px; font-size: 13px; font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted-2);
}
.lib-brand {
  display: flex; align-items: baseline; gap: 9px;
  margin: 12px 0 8px; font-size: 15px; font-weight: 500; color: var(--ink);
}
.lib-group .lib-brand:first-of-type { margin-top: 8px; }
.lib-fam {
  font-family: "Roboto Mono", monospace; font-size: 11.5px;
  background: var(--fill); border-radius: 6px; padding: 3px 7px; white-space: nowrap;
}


/* --- the price list, by control unit -------------------------------------------
   One controller at a time on .navtabs, because four stacked lists was every
   price in the shop on one screen. The count rides in the tab so the strip
   says how much is behind each one without opening it. */
.price-group { margin-bottom: 20px; }
.price-ctrl {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: 0 0 10px; font-size: 13px; font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted-2);
}
.price-ctrl .hint { text-transform: none; letter-spacing: 0; font-weight: 400; }

.navtabs .tab-n {
  margin-left: 7px; font-family: "Roboto Mono", monospace; font-size: 11px;
  color: var(--muted-2); background: var(--card); border-radius: 5px; padding: 1px 5px;
}
.navtabs a[aria-current="page"] .tab-n { color: var(--ink); background: var(--fill-2); }
.ctrl-note { margin: -8px 0 14px; font-size: 13px; color: var(--muted-2); }

/* --- a tick box that carries its own label -------------------------------------
   .tick is a bare square for a matrix cell; this is the same idea where the
   value needs saying -- the four control units a solution is offered for. Fill
   when on, never an edge: the house rule at the foot of this file. */
.ctrl-pick { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
.tickchip {
  display: inline-flex; align-items: center;
  background: var(--fill); color: var(--muted);
  border-radius: 7px; padding: 5px 10px; cursor: pointer;
  font-size: 12px; font-weight: 500; line-height: 1; white-space: nowrap;
  transition: background .12s ease, color .12s ease;
}
.tickchip input {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.tickchip:hover:not(:has(input:checked)) { background: var(--fill-2); color: var(--ink); }
.tickchip:has(input:checked) { background: var(--red); color: #fff; }
.tickchip:focus-within { box-shadow: 0 0 0 2px rgba(var(--red-rgb), .6); }

.lib-brand .lib-sep { color: var(--muted-2); font-weight: 400; }

/* The catalogue widget's own status line: what it is doing, or why it is not
   there. Hidden the moment its iframe arrives. */
.embed-state { margin: 0; padding: 18px 2px; font-size: 13px; color: var(--muted-2); line-height: 1.6; }
.embed-state.failed { color: var(--muted); }

/* --- the "sent" dialog -----------------------------------------------------
   One tick, one sentence, two ways out. Fill, not edge, for the tick: the
   house rule at the foot of this file. */
/* A confirmation, not a panel: narrow enough that the sentence wraps and the
   eye lands on the tick, the car, the figure, the two buttons -- in that
   order, down the middle. */
dialog.modal-narrow { width: min(400px, calc(100vw - 40px)); }
dialog.modal-narrow:focus { outline: none; }
/* The foot matches the head: .modal-hd sits 17px from the top edge, so the
   buttons sit the same from the bottom, rather than on it. */
.sent-bd { padding: 4px 20px 20px; text-align: center; }
.sent-ok {
  width: 48px; height: 48px; border-radius: 50%; margin: 2px auto 16px;
  display: grid; place-items: center;
  background: var(--red); color: #fff;
}
.sent-ok svg { width: 24px; height: 24px; }
.sent-lead { margin: 0 0 10px; font-size: 16px; font-weight: 500; line-height: 1.4; color: var(--ink); }
/* The figure on a line of its own, the largest thing in the dialog after the
   tick: it is what the dealer agreed to. */
.sent-amount { margin: 0 0 2px; font-size: 30px; font-weight: 500; letter-spacing: -.01em; color: var(--ink); line-height: 1.2; }
.sent-money { margin: 0 0 10px; font-size: 14px; line-height: 1.55; color: var(--muted); }
.sent-bd .hint { margin: 0 0 20px; font-size: 12.5px; color: var(--muted-2); }
.sent-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.sent-actions .btn, .sent-actions .btn-red { min-width: 140px; }
/* The link is dressed as the quiet button beside the red one, or it reads as
   a stray underlined word in a row of buttons. */
.sent-actions a.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 10px 16px; border-radius: 9px; text-decoration: none;
  background: var(--fill); color: var(--ink); font-size: 13.5px; font-weight: 500;
}
.sent-actions a.btn:hover { background: var(--fill-2); }

/* --- the request thread as a chat ----------------------------------------------
   Only .tl.chat: the ledger timeline and the ticket threads keep the dot
   layout. Own messages right, the other party's left, system notes centred
   as captions. Fills, never outlines, per the house rule at the foot of this
   file; the "tail" is a tighter corner on the side the bubble came from. */
.tl.chat .tl-item { display: flex; border-top: 0; padding: 7px 0; gap: 0; }
.tl.chat .tl-item:first-child { padding-top: 0; }
.tl.chat .mark { display: none; }
.tl.chat .tl-item > div {
  max-width: min(78%, 640px);
  padding: 10px 14px 11px;
  border-radius: 14px;
  background: var(--fill-2);
}
.tl.chat .tl-item.them > div { border-bottom-left-radius: 4px; }
.tl.chat .tl-item.me { justify-content: flex-end; }
.tl.chat .tl-item.me > div { background: var(--red-soft); border-bottom-right-radius: 4px; }
.tl.chat .tl-item.internal > div { background: rgba(255, 159, 10, .13); }

/* Who and when: smaller inside a bubble, the bubble already says which side. */
.tl.chat .who { gap: 8px; margin-bottom: 1px; }
.tl.chat .who b { font-size: 12.5px; font-weight: 500; color: var(--muted); }
.tl.chat .who time { font-size: 11px; }
.tl.chat .tl-item > div > p { margin-top: 3px; color: var(--ink); font-size: 14.5px; line-height: 1.5; }
.tl.chat .tl-item.me .who { justify-content: flex-end; }
.tl.chat .tl-item.me .who b { color: var(--red-ink); }

/* A system note is a caption between bubbles, not a bubble. */
.tl.chat .tl-item.sys { justify-content: center; padding: 4px 0; }
.tl.chat .tl-item.sys > div {
  max-width: 90%; background: none; padding: 2px 8px; text-align: center;
}
.tl.chat .tl-item.sys .who { justify-content: center; }
.tl.chat .tl-item.sys .who b { color: var(--muted-2); font-size: 11.5px; }
.tl.chat .tl-item.sys > div > p { color: var(--muted-2); font-size: 13px; margin-top: 1px; }

/* Attachment chips sit inside the bubble and wrap with it. */
.tl.chat .att-chips { margin-top: 9px; }
.tl.chat .tl-item.me .att-chips { justify-content: flex-end; }

/* The pinned upload entry carries its request facts; a little more room. */
.tl.chat .tl-item.opening > div { max-width: min(88%, 720px); }
@media (max-width: 640px) { .tl.chat .tl-item > div, .tl.chat .tl-item.opening > div { max-width: 94%; } }

/* Inside a bubble the file chips are buttons, and buttons here are full red:
   a grey chip on the red tint read as disabled. White glyph and size on red;
   the Unlock chip keeps its lock and its word, which is what tells it apart
   from a plain download. */
.tl.chat .att-chip,
.tl.chat .att-chip.log,
.tl.chat .att-chip.pay { background: var(--red); color: #fff; }
.tl.chat .att-chip:hover { background: #ff5147; color: #fff; }   /* the house hover red, as .btn-red */
.tl.chat .att-chip svg,
.tl.chat .att-chip.log svg { color: #fff; }
.tl.chat .att-chip .att-size { color: rgba(255,255,255,.78); font-style: normal; }
.tl.chat .att-chip:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(255,255,255,.55); }

/* --- sortable column headers ----------------------------------------------------
   A header is a link that carries the sort. The active column is filled --
   colour says what is chosen, never an edge -- and its arrow gives the
   direction of the values; the others show their natural direction faintly
   so a click is never a surprise. */
th a.sort {
  display: inline-flex; align-items: center; gap: 5px;
  color: inherit; text-decoration: none;
  padding: 3px 7px; margin: -3px -7px; border-radius: 6px;
}
th a.sort .arrow { font-size: 9px; opacity: .35; transform: translateY(-1px); }
th a.sort:hover { background: var(--fill); color: var(--ink); }
th a.sort.on { background: var(--red); color: #fff; }
th a.sort.on .arrow { opacity: 1; }
th.r a.sort { flex-direction: row-reverse; }
th a.sort:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(var(--red-rgb), .6); }

/* --- specific prices (027) -------------------------------------------------------
   A price that applies to this car or this control unit rather than to all
   of them. Red says it is the chosen figure, the same way everywhere. */
.tag.spec { background: var(--red-soft); color: var(--red-ink);
            font-family: "Plus Jakarta Sans", sans-serif; font-weight: 500; font-size: 11.5px; }
.pick-p.spec { color: var(--red-ink); }

.lookup { margin-bottom: 14px; }
.lookup-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)) auto;
              gap: 12px; align-items: end; }
.lookup-row .field { margin: 0; }
.lookup-go { display: flex; gap: 8px; align-items: center; }
.lookup-go .btn, .lookup-go button { height: 40px; }
/* With script the pickers submit themselves; the button is the no-script path. */
html.js .lookup-go button[type="submit"] { display: none; }
.lookup-note { margin: 0; padding: 0 18px 14px; font-size: 13.5px; color: var(--muted); }
@media (max-width: 900px) { .lookup-row { grid-template-columns: 1fr 1fr; } }

.override-add { display: grid; grid-template-columns: 1.4fr repeat(4, 1fr) .8fr 1.2fr auto;
                gap: 10px; align-items: end; border-top: 1px solid var(--line); }
.override-add .field { margin: 0; }
.override-add .ov-go button { height: 40px; }
.override-add .ov-hint { grid-column: 1 / -1; margin: 0; }
@media (max-width: 1100px) { .override-add { grid-template-columns: repeat(3, 1fr); } }

/* --- price books (028) --------------------------------------------------------
   A figure a book inherits from the default is greyed; its own is full ink. */
input.inherited { color: var(--muted-2) !important; font-style: italic; }
.book-row td.veh { white-space: nowrap; }
.book-edit { display: flex; gap: 8px; align-items: center; }
.book-edit input { background: var(--bg); border: 0; border-radius: 8px; padding: 6px 10px;
                   color: var(--ink); font-family: inherit; font-size: 13.5px; min-width: 0; }
.book-edit input[name="name"] { width: 180px; }
.book-add { display: grid; grid-template-columns: .7fr 1.4fr .6fr 1.2fr auto; gap: 10px;
            align-items: end; border-top: 1px solid var(--line); }
.book-add .field { margin: 0; }
.book-add .bk-go button { height: 40px; }
.book-add .bk-hint { grid-column: 1 / -1; margin: 0; }
@media (max-width: 1000px) { .book-add { grid-template-columns: 1fr 1fr; } }
.matrix input.opx { width: 84px; background: var(--bg); border: 0; border-radius: 8px; padding: 5px 8px;
                    color: var(--ink); font-family: "Roboto Mono", monospace; text-align: right; font-size: 12.5px; }
.matrix td.op-solo { white-space: nowrap; }
.matrix td.op-solo button { margin-left: 4px; }

/* --- the car came back (030) ------------------------------------------------- */
.seen { display: flex; gap: 14px; align-items: flex-start; border-top: 1px solid var(--line);
        background: var(--red-soft); }
.seen-icon { flex: none; width: 34px; height: 34px; border-radius: 9px; background: var(--red);
             color: #fff; display: grid; place-items: center; }
.seen-icon svg { width: 18px; height: 18px; }
.seen-t { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; font-size: 13.5px; }
.seen-t b { font-weight: 600; }
.seen-t .hint { font-size: 12.5px; }
.seen-do { display: flex; gap: 8px; flex: none; align-items: center; }
.seen-do .btn, .seen-do button { height: 36px; }
@media (max-width: 760px) { .seen { flex-wrap: wrap; } .seen-do { width: 100%; justify-content: flex-end; } }

.adj-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 8px; margin-top: 8px; }
.adj-list .opt.done { opacity: .55; }
.adj-list .opt.done .opt-p { color: var(--green-ink); }
.drop.drop-slim { padding: 12px 14px; margin-top: 14px; }
.second-read.adjustment .eyebrow { color: var(--red-ink); }

/* --- currencies (032) ------------------------------------------------------ */
.book-fill { display: flex; gap: 8px; align-items: center; margin-left: auto; font-size: 13px; color: var(--muted); }
.book-fill select, .book-fill input { background: var(--bg); border: 0; border-radius: 8px; padding: 6px 10px;
                                      color: var(--ink); font-family: inherit; font-size: 13px; }
.bundle-add { display: flex; gap: 8px; align-items: center; border-top: 1px solid var(--line); }
.bundle-add input, .bundle-add select { background: var(--bg); border: 0; border-radius: 8px; padding: 8px 10px;
                                        color: var(--ink); font-family: inherit; font-size: 13.5px; min-width: 0; }
.bundle-add input[name="name"] { flex: 1; }

/* --- operator (035) ---------------------------------------------------------- */
.enrol { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; align-items: end; }
.enrol .field { margin: 0; }
.enrol .field .hint { display: block; margin-top: 4px; }
.enrol .en-go button { height: 40px; }
@media (max-width: 900px) { .enrol { grid-template-columns: 1fr 1fr; } }

/* --- branding (036) ------------------------------------------------------------ */
.brand-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.brand-slot { display: flex; flex-direction: column; gap: 10px; }
.brand-preview { background: var(--bg); border-radius: 10px; padding: 14px 18px; display: flex; align-items: center; min-height: 60px; }
.brand-preview img { max-width: 100%; height: auto; max-height: 48px; }
.brand-form { display: flex; gap: 8px; align-items: center; }
.brand-form input[type="file"] { flex: 1; min-width: 0; font-size: 13px; }
@media (max-width: 760px) { .brand-grid { grid-template-columns: 1fr; } }


/* ======================================================================
   chiptuning.ai BRAND LAYER — token overrides win here at the cascade end.
   ====================================================================== */
/* chiptuning.ai brand overlay — loaded AFTER app.css.
   Remaps the vendored X-Portal theme from red-on-grey to the chiptuning.ai
   brand: violet accent (#7c3aed) on a pure-black ground, matching the brand
   wallpaper (design/brand/). Because app.css derives --red / --red-soft and the
   auth-brand gradient from --red-rgb, overriding that one token re-tints every
   accent (buttons, focus rings, active nav, the brand panel). */

:root {
    /* THE ONE KNOB — change this to recolour the entire UI. Everything accent
       (buttons, focus rings, active nav, links, the brand-panel glow) derives
       from it. The base sheet's legacy --red* tokens alias to it. */
    --accent-rgb: 124, 58, 237;        /* #7c3aed chiptuning.ai violet */
    --red-rgb: var(--accent-rgb);

    /* Scalar aliases so consuming apps can be fully token-driven. */
    --accent: rgb(var(--accent-rgb));
    --accent-hover: #9a6bff;           /* lighter violet, matches the button gloss hover */
    --red-hover: var(--accent-hover);

    /* Font-stack tokens (the base sheet also sets these directly on body/.mono). */
    --fs: "Plus Jakarta Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
    --fm: "Roboto Mono", ui-monospace, Menlo, Consolas, monospace;
}

/* Brand ground = black. Scoped to dark (the login/app shell runs data-theme=dark),
   so light mode is untouched. Mirrors app.css's dark selectors. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #000000;
        --card: #121218;
        --card-2: #17171f;
        --card-3: #1e1e28;
        --rail: #000000;
        --red-ink: #b794f6;    /* violet as legible text on dark */
    }
}

:root[data-theme="dark"] {
    --bg: #000000;
    --card: #121218;
    --card-2: #17171f;
    --card-3: #1e1e28;
    --rail: #000000;
    --red-ink: #b794f6;
}

/* Brand lockup: rocket mark on a light chip + wordmark. */
.brand-lockup { display: flex; align-items: center; gap: 12px; }
/* No tile: render the (black-on-transparent) rocket as a clean white mark so it
   reads on the dark panel. */
.brand-chip { display: inline-flex; }
.brand-chip img {
    height: 36px; width: 36px; object-fit: contain; display: block;
    filter: brightness(0) invert(1);
}
.brand-word { font-size: 22px; font-weight: 600; letter-spacing: -.4px; color: var(--ink); }
.brand-word .tld { color: var(--red-ink); }

/* Anchor-buttons: make <a class="btn"> render as a real button (app.css gives
   .btn padding but no display, so on an <a> the padding overflows). */
a.btn {
    display: inline-flex; align-items: center; justify-content: center;
    text-decoration: none;
}

/* Primary (accent) buttons: glossy purple with a left→right shimmer on hover,
   replacing app.css's hardcoded red hover. */
.btn-red {
    position: relative;
    overflow: hidden;
    /* Glossy accent: a white gloss gradient over the solid accent colour, so it
       stays token-driven (recolours with --accent-rgb). */
    background:
        linear-gradient(180deg, rgba(255, 255, 255, .16), rgba(255, 255, 255, 0) 62%),
        rgb(var(--accent-rgb));
}
.btn-red::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(115deg,
        transparent 30%, rgba(255, 255, 255, .45) 50%, transparent 70%);
    transform: translateX(-160%);
    transition: transform .65s ease;
    pointer-events: none;
}
.btn-red:hover {
    background:
        linear-gradient(180deg, rgba(255, 255, 255, .27), rgba(255, 255, 255, .04) 62%),
        rgb(var(--accent-rgb));
}
.btn-red:hover::after { transform: translateX(160%); }
@media (prefers-reduced-motion: reduce) {
    .btn-red::after { transition: none; }
}

/* TOTP enrolment: QR + details sit side by side; the QR is a fixed, self-sized
   image so it can never overflow onto the rest of the panel. */
.tfa-grid { display: flex; gap: 28px; flex-wrap: wrap; align-items: flex-start; }
.qr-img {
    flex: 0 0 auto; width: 188px; height: 188px;
    background: #fff; border-radius: 10px; padding: 8px; box-sizing: border-box;
    display: block;
}
.qr-aside { flex: 1 1 260px; min-width: 240px; }

/* On pure black, lift inputs off the card so they're clearly tappable. */
:root[data-theme="dark"] .field input,
:root[data-theme="dark"] .field select,
:root[data-theme="dark"] .field textarea {
    background: #0b0b10;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .07);
}
