/* ===== Design tokens mirror the mobile app (allquote-rn/theme.js) so web and
   app read as one product: same teal brand, neutral dark surfaces, soft 14px
   corners, thin rgba borders, high-contrast text. Light mode = app's `light`
   palette; dark overrides below = app's `dark` palette. ===== */
:root{
  --brand-spring:#2EA98C; --brand-core:#118173; --brand-pine:#0A514A; --brand-ink:#0C3F3A;
  --teal:var(--brand-core); --teal-dark:var(--brand-pine); --teal-light:#e9f5f2;
  /* brandSoft (app theme.js light): a faint teal wash for active pills + the FAB
     glow. The app uses rgba(brand,0.10); we expose it as a token so cards, the
     sidebar active state and the FAB share ONE source of truth. */
  --brand-soft:rgba(17,129,115,.10);
  --ink:#1a1d21; --slate:#475569; --muted:#64748b;
  /* --text is an alias of --ink. ~106 declarations across 15 templates already say
     color:var(--text); with it undefined those resolved to an invalid value and fell
     back to `inherit`, so anything sitting inside a container with its own colour (the
     dark canvas toolbar in takeoff) silently inherited that instead of the body ink. */
  --text:var(--ink);
  --line:#E9EFED; --bg:#F6F8F7; --card:#ffffff;
  --green:#16a34a; --amber:#d97706; --red:#dc2626; --blue:#2563eb; --link:var(--brand-core);
  /* --danger and --bg2 were used across 26 sites with a HARDCODED light fallback and
     were never defined, so dark mode rendered a light-mode red / a near-white panel.
     --danger is an alias for the semantic red; --bg2 is the soft panel behind a card. */
  --danger:var(--red); --bg2:#f1f5f9;
  /* --danger is the FILL/border red. --danger-ink is red TEXT sitting on a danger-tinted
     surface, where the fill lifts the background and the plain red drops under 4.5:1
     (the overdue invoice row is exactly this: #dc2626 on rgba(220,38,38,.05) = 4.47). */
  --danger-ink:#b91c1c;
  --aq-wordmark-all:var(--brand-core); --aq-wordmark-quote:var(--brand-ink);
  /* Radius scale — 14/10/7 as before, PLUS --radius-lg (20px) to match the app's
     GlassCard.js (borderRadius:20). Cards/stat tiles use the larger corner. */
  --radius:14px; --radius-sm:10px; --radius-xs:7px; --radius-lg:20px;
  /* Spacing scale (4px base) — a consistent rhythm shared with the app's 16px
     card padding. Additive: existing fixed px values are unchanged. */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px;
  --control-h:40px; --label-gap:6px;
  --tile-grid-gap:14px;
  --shadow:0 1px 3px rgba(15,23,42,.08),0 8px 24px rgba(15,23,42,.06);
  /* Sidebar widths as tokens so the body padding stays in sync with the full rail
     and the collapsed icon rail. */
  --dsb-w:264px;
  --dsb-rail-w:64px;
  --app-sidebar-offset:0px;
  --page-gutter:clamp(16px,1.25vw,24px);
}
/* Both address widgets use the same readable pointer and keyboard states. */
.aq-address-option{display:block;width:100%;text-align:left;white-space:normal;border:0;border-radius:0;background:transparent;color:var(--ink);font:inherit;box-shadow:none}
.aq-address-option:hover,.aq-address-option:focus-visible{background:var(--bg2);color:var(--ink);outline:2px solid var(--teal);outline-offset:-2px}

/* ===== Dark mode (toggled in Settings; <html data-theme="dark">). The app is
   built on these variables, so most of the UI re-themes from the overrides here.
   Teal remains the dark accent for links/icons, active states and highlights; the
   app background and surfaces stay neutral charcoal. ===== */
html[data-theme="dark"]{
  /* Traditional dark palette: neutral backgrounds, Spring/Core Teal accents,
     off-white text, brighter semantic colours. */
  --teal:#2EA98C; --teal-light:rgba(46,169,140,.16); --raised:#26272b;
  /* Teal accent wash for active and focused states on the neutral dark surface. */
  --brand-soft:rgba(46,169,140,.16);
  --ink:#f4f5f6; --slate:#c9d0d6; --muted:#969ca6; --text:var(--ink);
  --line:#2e2f33; --bg:#121214; --card:#1d1e21; --link:#2EA98C;
  --green:#22c55e; --amber:#fbbf24; --red:#f87171; --blue:#60a5fa;
  --danger:var(--red); --bg2:var(--raised); --danger-ink:#fca5a5;
  /* Keep teal as an accent token only; ordinary text uses --ink/--slate/--muted. */
  --teal-dark:#2EA98C;
  --aq-wordmark-all:#2EA98C; --aq-wordmark-quote:#f4f5f6;
  --shadow:0 1px 3px rgba(0,0,0,.5),0 10px 28px rgba(0,0,0,.4);
}
html[data-theme="dark"]{color-scheme:dark}
/* Bottom nav: inactive icons stay neutral grey, only the current tab is teal. */
html[data-theme="dark"] .botnav a.active,
html[data-theme="dark"] .botnav a.bn-cta{color:var(--teal)}
html[data-theme="dark"] .pill,
html[data-theme="dark"] .lifecycle .lc.done,
html[data-theme="dark"] .orb-label,
html[data-theme="dark"] .brand .wordmark .w2{color:var(--aq-wordmark-quote)}
html[data-theme="dark"] .badge.draft{background:var(--raised);color:#cbd5e1}
html[data-theme="dark"] .lifecycle .lc{background:var(--raised);color:#b5bdc8}
html[data-theme="dark"] .li-row.review{background:#2c2710}
html[data-theme="dark"] .li-row.expanded{background:var(--raised);border:1px solid var(--line)}
html[data-theme="dark"] .compare tbody tr:nth-child(even){background:#202124}
html[data-theme="dark"] input,
html[data-theme="dark"] select,
html[data-theme="dark"] textarea{background:var(--raised);color:var(--ink)}
html[data-theme="dark"] input::placeholder,
html[data-theme="dark"] textarea::placeholder{color:var(--muted)}
/* Browser autofill forces a light background + dark text on inputs (the "light
   boxes" in dark mode). WebKit won't let you set the background directly, so paint
   over it with a huge inset box-shadow and override the text fill colour. */
html[data-theme="dark"] input:-webkit-autofill,
html[data-theme="dark"] input:-webkit-autofill:hover,
html[data-theme="dark"] input:-webkit-autofill:focus,
html[data-theme="dark"] input:-webkit-autofill:active,
html[data-theme="dark"] select:-webkit-autofill,
html[data-theme="dark"] textarea:-webkit-autofill{
  -webkit-text-fill-color:var(--ink);
  -webkit-box-shadow:0 0 0 1000px var(--raised) inset;
  box-shadow:0 0 0 1000px var(--raised) inset;
  caret-color:var(--ink);
  border:1px solid var(--line);
  transition:background-color 9999s ease-in-out 0s;
}
/* Filled accent controls with white text use Core Teal in dark mode; Spring Teal
   is too bright for white text (2.93:1). */
html[data-theme="dark"] .daygrid label:has(input:checked),
html[data-theme="dark"] .btn:not(.ghost):not(.danger),
html[data-theme="dark"] .banner,
html[data-theme="dark"] .tag,
html[data-theme="dark"] .botnav a.bn-cta .ic,
html[data-theme="dark"] .lifecycle .lc.cur,
html[data-theme="dark"] #aq-dialer .aq-dial-head,
html[data-theme="dark"] .seg.on,
html[data-theme="dark"] .seg-btn.on,
html[data-theme="dark"] .seg-b.on,
html[data-theme="dark"] .tg-dhead.today .dom,
html[data-theme="dark"] .stepnum,
html[data-theme="dark"] .invfilters .ctab.active,
html[data-theme="dark"] .ctab.active,
html[data-theme="dark"] .bub.out,
html[data-theme="dark"] .feat-mode input:checked + span,
html[data-theme="dark"] .dt-seg button.on,
html[data-theme="dark"] .pm-opt[data-on="1"],
html[data-theme="dark"] .infoi:hover,
html[data-theme="dark"] .infoi:focus-visible,
html[data-theme="dark"] .nba-act.nba-primary,
html[data-theme="dark"] .bt-save,
html[data-theme="dark"] .bt-opt.is-on,
html[data-theme="dark"] .ai-opt.is-on,
html[data-theme="dark"] .prog-badge,
html[data-theme="dark"] .verb,
html[data-theme="dark"] [style*="background:var(--teal);"][style*="color:#fff"],
html[data-theme="dark"] [style*="background:var(--teal,#118173);"][style*="color:#fff"]{
  --teal:#118173;
  --teal-dark:#118173;
}
html[data-theme="dark"] .banner .btn{background:#f4f5f6;color:#118173;border-color:transparent}
html[data-theme="dark"] .banner .btn:hover{background:#fff;color:#118173}
html[data-theme="dark"] .btn.danger:hover{background:#3b1115;border-color:#7f1d1d;color:#fecaca}
html[data-theme="dark"] .promo-code{background:var(--raised);color:#2EA98C;border:1px solid var(--line)}
html[data-theme="dark"] .demo-flash{background:var(--card)!important;border-color:rgba(46,169,140,.38)!important;color:var(--ink)!important}
/* Weekday picker: a fixed 7-across grid (equal, shrinkable cols so it never wraps
   OR overflows). The native checkbox is hidden; a ticked day shows as a teal chip. */
.daygrid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:5px}
.daygrid label{display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;
  padding:8px 0;border:1px solid var(--line);border-radius:7px;cursor:pointer;white-space:nowrap;overflow:hidden;user-select:none}
.daygrid input{position:absolute;opacity:0;width:0;height:0}
.daygrid label:has(input:checked){background:var(--teal);color:#fff;border-color:var(--teal)}
/* Compact header buttons: full label on desktop, icon-only on phones (no wrapping). */
.btn{white-space:nowrap}
@media (max-width:640px){ .btn-lbl{display:none} }
/* Collapsible cards — tap the header to fold everything under it. */
.card.cc-collapsed > *:not(:first-child){display:none !important}
.card .cc-chev{color:var(--muted)}
/* "Available now" toggle switch (dashboard). */
.av-track{position:absolute;inset:0;background:var(--line);border-radius:999px;transition:background .15s}
.av-track::after{content:"";position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;background:#fff;transition:left .15s ease;box-shadow:0 1px 3px rgba(0,0,0,.35)}
.av-switch .avail-switch-in:checked + .av-track{background:#22c55e}
.av-switch .avail-switch-in:checked + .av-track::after{left:25px}
html[data-theme="dark"] .acct-menu a:hover{background:var(--raised)}
html[data-theme="dark"] .brand .wordmark .w1{color:var(--aq-wordmark-all)}
html[data-theme="dark"] img.shadowed,
html[data-theme="dark"] .invoice-paper{filter:none}
*{box-sizing:border-box}
.ic{vertical-align:-.18em;flex:none}  /* inline line-icons (icon() macro) */
html,body{margin:0;padding:0;overflow-x:clip;max-width:100%;width:100%;overscroll-behavior-x:none;-webkit-text-size-adjust:100%;text-size-adjust:100%}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,Helvetica,Arial,sans-serif;
  color:var(--ink);background:var(--bg);line-height:1.55;-webkit-font-smoothing:antialiased}
a{color:var(--link);text-decoration:none}
a:hover{text-decoration:underline}
h1,h2,h3{line-height:1.2;margin:0 0 .4em}
/* Wave-4b #45: the ONE standard page header — title on the left, optional action
   buttons on the right, wrapping cleanly on narrow screens. Main pages use this
   block so every screen opens the same way. */
.page-head{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 var(--stack-tight)}
.page-head h1{margin:0;font-size:24px}
.page-head .page-actions{display:flex;gap:6px;flex-wrap:wrap;align-items:center;justify-content:flex-end}
.container{max-width:1080px;margin:0 auto;padding:0 20px}
/* Authenticated app pages use the whole sidebar-adjusted viewport. Public pages
   keep the marketing/document caps below. */
.authed main.container{width:100%;max-width:1600px;margin:0 auto;padding-left:var(--page-gutter);padding-right:var(--page-gutter);min-width:0}
/* Public wide pages keep the public-site cap; nested authenticated wide containers
   can still use the full capped main column. */
.container.wide{max-width:1080px}
.authed main.container.wide{max-width:1600px}
.authed .container.wide:not(main){max-width:1600px}
/* One page width everywhere when logged in — narrow stays for auth pages only */
.authed .narrow{max-width:1600px}
.narrow{max-width:760px}

/* Nav */
.nav{background:var(--card);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:40}
.nav .container{display:flex;align-items:center;gap:18px;height:62px}
.authed .nav .container{width:100%;max-width:1600px;margin:0 auto;padding-left:var(--page-gutter);padding-right:var(--page-gutter)}
.brand{font-weight:800;font-size:20px;color:var(--ink);display:flex;align-items:center;gap:calc(var(--aq-lockup-icon,34px) * .28)}
.brand .dot{width:26px;height:26px;border-radius:7px;background:var(--teal);display:inline-flex;
  align-items:center;justify-content:center;color:#fff;font-size:14px}
.aq-lockup{display:inline-flex;align-items:center;gap:calc(var(--aq-lockup-icon,34px) * .28);vertical-align:middle;text-decoration:none}
.aq-lockup-stacked{flex-direction:column;justify-content:center;gap:14px;text-align:center}
.aq-lockup-icon-wrap{display:inline-flex;align-items:center;justify-content:center;flex:none}
.brand .mark,.aq-lockup .mark{display:block;border-radius:calc(var(--aq-lockup-icon,34px) * .225);flex:none;width:var(--aq-lockup-icon,34px);height:var(--aq-lockup-icon,34px)}
.brand .wordmark,.aq-wordmark{font-family:Manrope,-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,Helvetica,Arial,sans-serif;font-weight:800;font-size:var(--aq-lockup-text,20px);line-height:1;letter-spacing:-.025em;color:#0C3F3A}
.brand .wordmark .w1,.aq-wordmark .aq-wordmark-all{color:var(--aq-wordmark-all,#118173)}
.brand .wordmark .w2,.aq-wordmark .aq-wordmark-quote{color:var(--aq-wordmark-quote,#0C3F3A)}
.aq-lockup-single .wordmark span{color:#0C3F3A}
.brand:hover{text-decoration:none}
.header-search-wrap{position:relative;display:flex;align-items:center;flex:0 1 430px;min-width:260px;max-width:460px;color:var(--muted);z-index:61}
.header-search{display:flex;align-items:center;position:relative;width:100%;color:var(--muted)}
.header-search .ic{position:absolute;left:12px;z-index:1;color:var(--muted);pointer-events:none}
.header-search>input[type=search]{width:100%;height:var(--control-h);border:1px solid var(--line);border-radius:10px;background:var(--card);
  color:var(--ink);font:inherit;font-size:14px;outline:0;padding-block:0;padding-inline:38px 48px}
.header-search input::placeholder{color:var(--muted)}
.header-search input:focus{border-color:var(--teal);background:var(--card);box-shadow:0 0 0 3px var(--brand-soft)}
.header-search .kbd{position:absolute;right:10px;font-size:11px;font-weight:800;color:var(--muted);
  border:1px solid var(--line);border-radius:6px;padding:1px 5px;background:var(--card);pointer-events:none}
.header-search-results{position:absolute;top:calc(100% + 8px);left:0;width:min(620px,calc(100vw - var(--app-sidebar-offset) - 40px));
  min-width:100%;max-height:min(460px,70vh);overflow-y:auto;background:var(--card);border:1px solid var(--line);
  border-radius:12px;box-shadow:0 18px 44px rgba(2,6,23,.20);padding:6px;color:var(--ink)}
.header-search-results[hidden]{display:none}
.hsearch-item{display:flex;align-items:center;gap:10px;padding:10px 11px;border-radius:9px;color:var(--ink);text-decoration:none;font-size:14px}
.hsearch-item:hover,.hsearch-item.sel{background:var(--brand-soft);text-decoration:none}
.hsearch-item .cmdk-path{display:flex;align-items:center;gap:6px;flex:1;min-width:0;flex-wrap:wrap;line-height:1.35}
.hsearch-item .cmdk-crumb{display:inline-flex;align-items:center;gap:4px;white-space:nowrap}
.hsearch-item .cmdk-crumb .ic{position:static;flex:0 0 auto;color:var(--muted);pointer-events:none}
.hsearch-item .cmdk-sep{color:var(--muted);font-size:12px}
.hsearch-divider{margin:6px 4px 3px;padding-top:7px;border-top:1px solid var(--line);color:var(--muted);font-size:10px;font-weight:800;letter-spacing:.05em;text-transform:uppercase}
.hsearch-empty{padding:14px 12px;color:var(--muted);font-size:14px;text-align:center}
.header-search-icon{display:none;align-items:center;justify-content:center;width:var(--control-h);height:var(--control-h);border:1px solid var(--line);
  border-radius:10px;background:var(--card);color:var(--muted);cursor:pointer;padding:0;flex:0 0 auto}
.header-search-icon:hover{color:var(--teal);background:var(--bg)}
.nav .spacer{flex:1}
.nav a.navlink{color:var(--slate);font-weight:600;font-size:15px;white-space:nowrap}
.nav a.navlink:hover{color:var(--teal);text-decoration:none}
.pill{font-size:12px;font-weight:700;padding:3px 9px;border-radius:999px;background:var(--teal-light);color:var(--teal-dark)}
/* Nav: secondary links flow inline on desktop, collapse into a menu on phones. */
.nav-extra{display:contents}
.nav-burger{display:none;background:none;border:1px solid var(--line);border-radius:9px;
  width:38px;height:38px;padding:0;cursor:pointer;color:var(--ink);
  align-items:center;justify-content:center;flex:0 0 auto}
.nav-burger:hover{background:var(--bg)}
.nav-burger:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
/* Mobile: compact bar (logo + Dashboard / Invoices / + Quote + hamburger). */
@media(max-width:720px){
  .nav{position:relative}
  .nav .container{flex-wrap:nowrap;gap:8px}
  .nav .brand .wordmark{display:none}
  .header-search-wrap{display:none}
  .header-search-icon{display:inline-flex}
  .nav a.navlink,.nav .pill{font-size:13px}
  .banner{padding:28px 20px}
  .banner h2{font-size:23px}

  /* ── Public (signed-out) nav on phones ──────────────────────────────────────
     The marketing bar carries the brand, five links and the Start free CTA. Laid
     out inline at 375px it measured 464px against a 375px viewport, and html/body
     are overflow-x:clip, so the CTA sat off the right edge: still visible to a
     screen reader and still in the tab order, but impossible to see or tap. At
     320px "Log in" was clipped too. On /faq and /guide the nav CTA is the only
     signup link on the page, so those pages had no reachable signup path at all.

     Two layers, deliberately:
       1. The bar WRAPS. A longer label, a fallback font or the reader's zoom can
          no longer push anything off-screen — overflow is structurally impossible
          rather than a pixel budget that happens to balance.
       2. html.aq-navjs (set synchronously before first paint) folds the four
          secondary links into the Menu dropdown so the bar is one tidy row again.
          Brand, Log in and the CTA never leave the bar.
     With JS off you get layer 1 alone: plainer and taller, still complete. Nothing
     here hides the CTA on mobile, and nothing shrinks a font to make it fit. */
  .nav.pubnav .container{flex-wrap:wrap;height:auto;min-height:56px;row-gap:4px;
    padding-top:6px;padding-bottom:6px}
  html.aq-navjs .nav.pubnav .container{flex-wrap:nowrap;height:56px;row-gap:0;
    padding-top:0;padding-bottom:0}
  html.aq-navjs .nav.pubnav .nav-burger{display:inline-flex}
  html.aq-navjs .nav.pubnav .nav-extra{display:none}
  html.aq-navjs .nav.pubnav .nav-extra.open{display:flex;flex-direction:column;position:absolute;
    top:calc(100% - 1px);right:8px;background:var(--card);border:1px solid var(--line);
    border-radius:12px;box-shadow:var(--shadow);padding:8px;min-width:190px;z-index:50;gap:2px}
  html.aq-navjs .nav.pubnav .nav-extra.open a.navlink{padding:10px 12px;border-radius:8px;font-size:15px}
  html.aq-navjs .nav.pubnav .nav-extra.open a.navlink:hover{background:var(--bg);text-decoration:none}
}

/* Buttons */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;
  min-height:var(--control-h);font-weight:700;font-size:15px;padding:0 18px;border-radius:10px;border:1px solid transparent;
  background:var(--teal);color:#fff;transition:.15s;text-decoration:none;
  white-space:nowrap}            /* never wrap/break a button label mid-word */
.btn:hover{background:var(--teal-dark);text-decoration:none}
.btn.lg{padding:14px 26px;font-size:17px}
.btn.block{width:100%}
.btn.ghost{background:var(--card);color:var(--ink);border-color:var(--line)}
.btn.ghost:hover{background:var(--bg);border-color:var(--muted)}
.btn.danger{background:var(--card);color:var(--red);border-color:#fecaca}
.btn.danger:hover{background:#fef2f2}
.btn.sm{height:var(--control-h);padding:0 12px;font-size:13px}
.btn.rec{background:var(--red);border-color:var(--red);color:#fff;animation:pulse 1.3s ease-in-out infinite}
.btn.rec:hover{background:#b91c1c}
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 rgba(220,38,38,.45)}50%{box-shadow:0 0 0 6px rgba(220,38,38,0)}}
/* Voice input: an animated sound-wave instead of a mic emoji, and an AI gradient
   glow while listening instead of a red recording light. */
.wave{display:inline-flex;align-items:center;gap:2px;height:16px}
.wave span{width:2.5px;border-radius:2px;background:currentColor}
.wave span:nth-child(1){height:5px}.wave span:nth-child(2){height:11px}
.wave span:nth-child(3){height:16px}.wave span:nth-child(4){height:9px}
.wave span:nth-child(5){height:6px}
.btn.listening{background:linear-gradient(120deg,var(--teal),#6366f1);border-color:transparent;color:#fff;
  animation:aiglow 1.7s ease-in-out infinite}
.btn.listening .wave span{animation:eq .9s ease-in-out infinite}
.btn.listening .wave span:nth-child(2){animation-delay:.12s}
.btn.listening .wave span:nth-child(3){animation-delay:.24s}
.btn.listening .wave span:nth-child(4){animation-delay:.36s}
.btn.listening .wave span:nth-child(5){animation-delay:.48s}
@keyframes eq{0%,100%{height:4px}50%{height:16px}}
@keyframes aiglow{0%,100%{box-shadow:0 0 0 0 rgba(99,102,241,.5)}50%{box-shadow:0 0 0 8px rgba(99,102,241,0)}}
@media(prefers-reduced-motion:reduce){.btn.listening,.btn.listening .wave span{animation:none}}
/* ChatGPT-style voice orb: a greeny-blue orb drops over the job box while listening. */
.voice-field{position:relative}
.voice-orb{position:absolute;inset:0;z-index:6;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:16px;border-radius:10px;background:rgba(248,250,252,.82);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);
  opacity:0;transform:translateY(-22px);pointer-events:none;transition:opacity .3s ease,transform .45s cubic-bezier(.2,.8,.2,1)}
.voice-field.listening .voice-orb{opacity:1;transform:translateY(0);pointer-events:auto;cursor:pointer}
.orb-wrap{position:relative;width:104px;height:104px}
.orb{position:absolute;inset:0;border-radius:50%;
  background:radial-gradient(circle at 33% 30%,#6ee7d6 0%,#22d3ee 40%,#6366f1 88%);
  box-shadow:0 12px 50px rgba(56,189,248,.55);animation:orb-breathe 2.6s ease-in-out infinite}
.orb::after{content:"";position:absolute;inset:-28%;border-radius:50%;z-index:-1;
  background:conic-gradient(from 0deg,#34d399,#22d3ee,#6366f1,#34d399);filter:blur(20px);opacity:.6;
  animation:orb-spin 6s linear infinite}
.orb-label{font-weight:700;color:var(--ink);font-size:14px}
@keyframes orb-breathe{0%,100%{transform:scale(1)}50%{transform:scale(1.1)}}
@keyframes orb-spin{to{transform:rotate(360deg)}}
@media(prefers-reduced-motion:reduce){.voice-orb{transition:opacity .2s}.orb,.orb::after{animation:none}}

/* Cards — matched to the app's GlassCard.js (borderRadius:20, 1px border). The
   larger --radius-lg corner + hairline --line border + soft --shadow read like the
   RN surface. Padding stays generous (24px desktop; the mobile block trims it). */
/* ONE RULE OWNS THE VERTICAL RHYTHM OF A PAGE.
   Every top-level block on a page is separated from the next by --stack.

   It replaces an ALLOWLIST of adjacent-sibling pairs (.card + .card, .card + .tile,
   .tile + .card, ... just below). That allowlist named 7 classes -- 49 ordered pairs --
   and covered 14 of them, which turned out to be 64 of the 427 real top-level
   adjacencies in templates/, about 15%. Everything else fell through to whatever ad-hoc
   margin the two elements happened to carry, so one page measured 18 / 18 / 0 / 12 / 14
   between consecutive blocks and a card sat flush against a tab bar on /jobs. New markup
   could never be on a list written before it existed.

   margin-BOTTOM, not margin-top. base.html injects a <script> into <main> before
   `block body`, and 98 of 225 page templates open that block with a <style>, <link> or
   <script>. A margin-TOP owl (`> * + *`) therefore matches the first VISIBLE block
   against one of those invisible predecessors and adds a gap at the top of every page --
   measured on 1 Oct 2026 as 24px -> 42px on /settings and 36px -> 42px elsewhere. An
   element with no box contributes no bottom margin, so the margin-bottom form cannot do
   that. (<main> carries padding-bottom:60px, so the last child's margin stays inside it;
   the footer gap goes 60px -> 78px and nothing collapses out.)

   :where() gives this ZERO specificity, so it is a FLOOR, never an override. Every
   component margin, .mt/.mt2, and every inline style in the templates still wins --
   including the 32px .mt2 blocks, which a margin-top owl would have silently crushed to
   18px. Deliberately tight relationships (.page-head's 6px, h1-h3's .4em) survive by
   construction instead of by enumeration.

   Margins collapse here (main.container is a plain block, never flex or grid), so the
   result is max(previous bottom, next top) -- a gap can never double. */
/* THE WRAPPER LIST. A page either puts its blocks straight into main.container or wraps
   them in ONE full-width shell; the rule has to reach through that shell or it never
   engages at all. Measured 1 Oct 2026: /maintenance (main.mc-shell) was 7 of 7 off and
   /assets/testing (div.wide) 3 of 3, because neither wrapper was named here. The public
   pages wrap the same way -- .help-shell (/faq, /help/faq), .tutorial-shell
   (/help/tutorials), .guide-shell (/guide), .tq-shell (/guide/takeoff-quickstart).
   .wide reaches exactly three pages: it is overwhelmingly a container_class
   (main.container.wide, already matched by the first branch) and the only three
   `<div class="wide">` wrappers are /assets/testing, stock labels and the QR-batch admin
   tool. Every other `wide` token in templates/ is a .btn, a `field wide` form cell or a
   grid-span inside a form, none of which is a direct child of main.container.

   DELIBERATELY NOT HERE, both verified rather than assumed:
   .ts-page (/takeoff-software) is a band page like the landing page -- .ts-band carries a
   background and hairline top/bottom borders and .ts-section spaces itself with
   `padding:72px 0`, so its 5 flush seams are the design; an 18px gap would show the page
   background as a stripe between the bands.
   .api-doc (/developers) and .narrow's legal pages (/terms, /privacy) are running PROSE --
   their top-level children are the <p>, <h2> and <pre> of a document, where spacing is
   typography and not block rhythm. (.narrow is listed, so the token already applies there
   and deliberately loses to the heading/paragraph margins, which is the right outcome.) */
:root{ --stack:18px; --stack-tight:6px }
:where(main.container,
       main.container > :is(.narrow, .dash-hybrid, .po-list-page, .wide, .mc-shell,
                            .help-shell, .tutorial-shell, .guide-shell, .tq-shell)
      ) > *{ margin-bottom:var(--stack) }

/* A heading and its OWN caption hug; two blocks do not. Before this, .page-head carried
   margin-bottom:6px and the caption after it carried whatever the template author typed
   -- `margin:6px 0 14px`, `margin:2px 0 12px`, `margin:0 0 8px`, measured across six
   pages. The pairing was real and worth keeping; expressing it 35 different ways was not.
   The caption still gets the FULL stack below it, so the block after the caption keeps
   the page rhythm. */
:where(main.container,
       main.container > :is(.narrow, .dash-hybrid, .po-list-page, .wide, .mc-shell,
                            .help-shell, .tutorial-shell, .guide-shell, .tq-shell)
      ) > :is(.page-head, .dash-head, .po-list-head, .cat-header, h1, h2){
  margin-bottom:var(--stack-tight);
}
:where(main.container,
       main.container > :is(.narrow, .dash-hybrid, .po-list-page, .wide, .mc-shell,
                            .help-shell, .tutorial-shell, .guide-shell, .tq-shell)
      )
  > :is(.page-head, .dash-head, .po-list-head, .cat-header, h1, h2)
  + :is(p.hint, p.muted, .hint, .muted){
  margin-top:0;
  margin-bottom:var(--stack);
}

/* Opt-outs. --stack inherits, so one declaration silences a whole page from the
   container_class block the templates already use -- no markup edits.
   landing: 9 <section class="lp-section"> are alternating coloured bands with hairline
   borders, designed to butt together; a gap shows the page background as a stripe.
   takeoff / takeoff3d: full-bleed tools the owner ruled must never scroll in full screen. */
main.landing-shell,
main.takeoff-container,
main.takeoff3d-container{ --stack:0 }
.stack-flush{ --stack:0 }          /* a subtree stays edge-to-edge */
.flush{ margin-bottom:0 }          /* one element stays flush */

.card{display:flow-root;background:var(--card);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:24px;box-shadow:var(--shadow)}
.card + .card{margin-top:18px}
.card + .tile,
.tile + .card,
.tile + .tile,
.card + .tiles,
.card + .cards,
.card + .toolgrid,
.card + .report-grid,
.card + .schedule-wide-grid,
.tiles + .card,
.cards + .card,
.toolgrid + .card,
.report-grid + .card,
.schedule-wide-grid + .card{margin-top:18px}
/* ...but cards inside grid/tile boards are spaced by the grid GAP, so this
   stacked-card top margin would push every grid item after the first downward.
   Keep the reset broad because pages use .grid plus local tile containers. */
.grid > .card + .card,
.tiles > .card + .card,
.cards > .card + .card,
.toolgrid > .card + .card,
.flex > .card + .card,
.report-grid > .card + .card{margin-top:0}
.grid{display:grid;gap:18px;align-items:stretch}
.tiles,.cards,.toolgrid,.report-grid{display:grid;gap:var(--tile-grid-gap,14px);align-items:stretch}
.grid>*,.tiles>*,.cards>*,.toolgrid>*,.report-grid>*{min-width:0}
.chip-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0;max-width:100%}
.chip-row>*{min-width:0}
.chip-row .badge,.chip-row button,.chip-row a{white-space:normal;overflow-wrap:anywhere}
.grid > .card,
.tiles > .card,
.cards > .card,
.report-grid > .card{height:100%}
.grid.cols-2{grid-template-columns:1fr 1fr}
.grid.cols-3{grid-template-columns:repeat(3,1fr)}
.grid.cols-4{grid-template-columns:repeat(4,1fr)}
@media(max-width:780px){.grid.cols-2,.grid.cols-3{grid-template-columns:1fr}.grid.cols-4{grid-template-columns:1fr 1fr}}
/* Quote editor: 2-up (lines | side panel) on desktop, single scrollable column
   on mobile. (Was an inline grid-template-columns that ignored the breakpoint.) */
/* TRUE single column: Totals bar on top, then the editor stream. (Was a 2-col grid + right rail.) */
.quote-grid{display:flex;flex-direction:column;gap:18px}
.quote-totals-bar{margin:0}
/* Let columns shrink below their content so the line-item controls row scrolls
   within itself (overflow-x) instead of widening the whole page on mobile. */
.quote-grid>*{min-width:0}
#litable,.li-row{min-width:0}
.btn-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}

/* Shared toolbar controls: fields and commands keep one stable metric, while
   flexible children are allowed to shrink instead of widening the page. */
.control-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0;max-width:100%}
.control-row>*{min-width:0}
.control-row .control-grow{flex:1 1 220px;min-width:min(220px,100%)}
.control-row>.btn,.control-row>form>.btn{height:var(--control-h)}
.search-control{position:relative;display:flex;align-items:center;flex:1 1 220px;min-width:min(220px,100%);max-width:100%;margin:0}
.search-control>.ic{position:absolute;left:12px;z-index:1;color:var(--muted);pointer-events:none}
.search-control>input[type=search]{min-width:0;width:100%;padding-inline-start:38px}

/* Popover's UA box must not add padding/border to an existing dropdown. Zero
   specificity lets each component's current author styles continue to win. */
:where(.aq-anchored-menu[popover]){border:0;padding:0;color:inherit}

/* ===================================================================
   NAVIGATION COMPONENTS. There are three, and only three. If you are
   about to write a fourth, you are about to recreate the problem these
   replaced: 118 hand-rolled back controls in 15 class/style shapes across
   101 templates, and 22 distinct tab-bar class names nobody could tell
   apart - measured on main @3885699d, 1 Oct 2026.

   .aq-back            - the back control.      templates/_back.html
   .aq-section-tabs    - a row of SECTION tabs. templates/_section_tabs.html
   .segmented-control  - a VIEW switch.         this file, below.

   tests/test_nav_components.py fails the build, by file and class name,
   if a template grows a fourth.
   =================================================================== */

/* THE back control - rendered only by the aq_back() macro in
   templates/_back.html, which is where the usage notes live. Back is a quiet
   link because it is never the page's primary action; it is never a button.
   No margin of its own: the page's own header spacing positions it, the same
   way the quiet links it replaced were positioned. */
.aq-back{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:650;
  color:var(--muted);text-decoration:none;line-height:1.4;max-width:100%;
  overflow-wrap:anywhere}
.aq-back:hover,.aq-back:focus-visible{color:var(--ink);text-decoration:none}
.aq-back:focus-visible{outline:2px solid var(--teal);outline-offset:3px;border-radius:4px}

/* SECTION TABS. Rendered by templates/_section_tabs.html for link tab bars;
   the classes live HERE rather than in that macro's inline <style> so a
   JS-driven <button> tab bar (forms, safety, equipment detail) can use the
   same component instead of inventing a fifth underline. Active state is
   whichever of .on / .active / aria-selected / aria-current the bar already
   uses, so migrating one is a rename and not a rewrite. */
.aq-section-tabs{display:flex;gap:2px;border-bottom:1px solid var(--line);
  margin:0 0 var(--stack);overflow-x:auto;scrollbar-width:thin;max-width:100%}
.aq-section-tab{display:inline-flex;align-items:center;gap:6px;padding:9px 14px;border:0;
  background:none;cursor:pointer;font:inherit;font-weight:600;color:var(--muted);
  border-bottom:2px solid transparent;font-size:14px;white-space:nowrap;text-decoration:none;
  flex:0 0 auto}
.aq-section-tab:hover{color:var(--ink);text-decoration:none}
/* Selected: INK label, teal UNDERLINE. The standing rule is that brand teal is
   for fills and thin accent borders only and never for type, so the accent is the
   border and the label stays neutral. Weight does the rest of the work, and keeps
   the selected tab distinct from :hover, which is already ink. */
.aq-section-tab.on,.aq-section-tab.active,
.aq-section-tab[aria-selected="true"],.aq-section-tab[aria-current="page"]{
  color:var(--ink);font-weight:700;border-bottom-color:var(--teal)}
.aq-section-tab .aq-tab-badge{font-size:11px;padding:1px 7px;border-radius:999px;
  background:var(--brand-soft);color:var(--teal);font-weight:700;line-height:1;
  font-variant-numeric:tabular-nums}

/* SECTION TABS vs VIEW SWITCH - the distinction twelve bespoke idioms grew
   because nobody wrote it down. Write it down:

   .aq-section-tabs (templates/_section_tabs.html) is for a row of SECTION
   tabs: each tab is a different part of a hub, usually a different URL, and
   there can be many of them. Underline-on-the-active-one, scrolls sideways
   when it overflows. Jobs / Quotes / Invoices. Settings / Team / Billing.
   If adding a seventh tab would be reasonable, it is section tabs.

   .segmented-control (below) is for a two-or-three-way VIEW SWITCH over the
   SAME content: Pipeline / List, Day / Week / Month, Chart / Table. A filled
   pill group, sized like the other controls in a toolbar so it sits in a
   .control-row beside a search box. If a fourth option would feel crowded,
   that is correct - it should, and you want section tabs instead.

   Compact page-view selectors share the same geometry and remain above their
   associated content on narrow screens. */
.segmented-control{display:inline-flex;max-width:100%;border:1px solid var(--line);border-radius:8px;overflow:hidden;background:var(--card)}
.segmented-control>button,.segmented-control>a{height:var(--control-h);padding:0 15px;border:0;border-right:1px solid var(--line);background:var(--card);color:var(--muted);font:inherit;font-size:13px;font-weight:750;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:6px;text-decoration:none;white-space:nowrap}
.segmented-control>:last-child{border-right:0}
/* .full: the switch fills its column instead of hugging its labels - for a
   narrow sidebar panel, where inline-flex would leave a ragged right edge. */
.segmented-control.full{display:flex;width:100%}
.segmented-control.full>button,.segmented-control.full>a{flex:1 1 0;min-width:0}
.segmented-control>.on,.segmented-control>[aria-selected="true"],.segmented-control>[aria-current="page"]{background:var(--teal);color:#fff}
@media(max-width:620px){
  .control-row{align-items:stretch}
  .control-row .control-grow,.search-control{flex-basis:100%;min-width:0}
  .segmented-control{display:flex;width:100%}
  .segmented-control>button,.segmented-control>a{flex:1 1 0;min-width:0;padding-inline:8px}
}

/* Forms */
label{display:block;font-weight:600;font-size:14px;margin:0 0 var(--label-gap);color:var(--ink)}
.hint{font-size:13px;color:var(--muted);font-weight:400}
input:not([type]),input[type=text],input[type=email],input[type=password],input[type=number],input[type=color],input[type=tel],
input[type=date],input[type=time],input[type=datetime-local],input[type=search],input[type=url],
select,textarea{width:100%;padding:11px 13px;border:1px solid var(--line);border-radius:10px;
  box-sizing:border-box;font-size:15px;font-family:inherit;background:var(--card);color:var(--ink)}
input:not([type]),input[type=text],input[type=email],input[type=password],input[type=number],input[type=tel],
input[type=date],input[type=time],input[type=datetime-local],input[type=search],input[type=url],
select:not([multiple]):not([size]){min-height:var(--control-h);padding-block:7px;line-height:1.35}
input:not([type]),input[type=text],input[type=email],input[type=password],input[type=number],input[type=tel],
input[type=date],input[type=time],input[type=datetime-local],input[type=search],input[type=url]{height:var(--control-h)}
select:not([multiple]):not([size]){height:var(--control-h)}
input[type=file]{width:100%;max-width:100%;min-height:var(--control-h);height:var(--control-h);
  box-sizing:border-box;padding:4px 7px;border:1px solid var(--line);border-radius:var(--radius-sm);
  font:inherit;font-size:14px;line-height:1.35;color:var(--ink);background:var(--card);cursor:pointer}
input[type=file]::file-selector-button{margin-right:9px;padding:5px 10px;border:0;
  border-radius:var(--radius-xs);font:inherit;font-weight:700;line-height:1.35;
  color:var(--teal-dark);background:var(--teal-light);cursor:pointer}
input[type=file]::-webkit-file-upload-button{margin-right:9px;padding:5px 10px;border:0;
  border-radius:var(--radius-xs);font:inherit;font-weight:700;line-height:1.35;
  color:var(--teal-dark);background:var(--teal-light);cursor:pointer}
input[type=file]:hover::file-selector-button,
input[type=file]:focus-visible::file-selector-button{background:color-mix(in srgb,var(--teal) 18%,var(--card))}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--teal);
  box-shadow:0 0 0 3px var(--teal-light)}
textarea{resize:vertical;min-height:120px}
/* Colour selectors — STANDING RULE: the chosen colour ALWAYS shows as a FILLED
   rounded blotch, never the browser's thin colour bar or a hollow ring. The native
   <input type=color> is stretched invisibly over the swatch so the OS picker still
   opens on click/keyboard. The hairline border keeps a white/pale swatch visible on
   white (light) and dark backgrounds. base.html JS repaints --sw on input; the inline
   --sw set in markup means it's filled before JS runs and with JS disabled. Size is
   overridable per use with an inline width/height. */
.colour-blotch{position:relative;display:inline-flex;width:34px;height:34px;flex:0 0 auto;
  border-radius:9px;border:1px solid var(--line);background:var(--sw,#118173);
  cursor:pointer;vertical-align:middle}
.colour-blotch:focus-within{outline:2px solid var(--teal);outline-offset:2px}
.colour-blotch input[type=color]{position:absolute;inset:0;width:100%;height:100%;margin:0;
  padding:0;border:0;opacity:0;cursor:pointer;background:transparent}
.field{margin-bottom:16px}
.field-control{display:flex;flex-direction:column;gap:var(--label-gap);min-width:0;margin:0;color:var(--ink)}
.field-control>input,.field-control>select,.field-control>textarea{margin:0}
.form-actions-row{display:flex;align-items:flex-end;gap:8px;flex-wrap:wrap}.form-actions-row>.field{margin:0;min-width:180px}.form-actions-row>.btn{height:var(--control-h);display:inline-flex;align-items:center;justify-content:center}
.num,input[type=number],input[type=date],input[type=time],input[type=datetime-local],.money,.pricing-row-amount,.bal-item .v{font-variant-numeric:tabular-nums lining-nums}
.row{display:flex;gap:14px}
.row > *{flex:1}
@media(max-width:640px){.row{flex-direction:column;gap:0}}
/* Wave-4b #9: .grid2 was used by the claims forms but never defined, so the inputs
   rendered as an unstructured stack. Standard 2-col-then-stack form grid. */
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:10px 14px}
@media(max-width:700px){.grid2{grid-template-columns:1fr}}
.grid2 > label{margin:0}

/* Flash */
.flash{padding:12px 16px;border-radius:10px;margin-bottom:14px;font-weight:600;font-size:14px}
.flash.success{background:#dcfce7;color:var(--ink)}
.flash.error{background:#fee2e2;color:#991b1b}
.flash.info{background:#dbeafe;color:#1e40af}

/* Badges */
.badge{font-size:12px;font-weight:700;padding:3px 9px;border-radius:999px;display:inline-block;white-space:nowrap}
.badge.draft{background:#f1f5f9;color:#475569}
.badge.sent{background:#dbeafe;color:#1e40af}
.badge.partially_refunded{background:#ccfbf1;color:#0f766e}
.badge.refunded{background:#e2e8f0;color:#334155}
.badge.accepted{background:#dcfce7;color:#166534}
.badge.declined{background:#fee2e2;color:#991b1b}
.badge.est{background:#fef3c7;color:#92400e}
.badge.invoiced{background:#e0e7ff;color:#3730a3}   /* job invoiced, not yet paid */
.badge.partial{background:#fef3c7;color:#92400e}    /* invoice part-paid */
.badge.complete{background:#bbf7d0;color:#14532d}   /* job paid in full → complete */
/* Semantic status pills. The .badge.* classes above already pair a light tint with a
   deep dark-mode tint, but 107 pills across 23 templates wrote BOTH colours inline, so
   they stayed a bright light patch on a dark page with no way for the theme to reach
   them. These name the same pairings so markup can use them instead of hardcoding.
   The dark values are lifted verbatim from the .badge.* overrides below -- no new
   colour is introduced here. Neutralising only the FOREGROUND would have been the
   obvious move and the wrong one: var(--ink) is #f4f5f6 in dark, which on an unchanged
   #dcfce7 fill is about 1.006:1. The fill has to move with the text. */
.pill-ok{background:#dcfce7;color:#166534}
.pill-warn{background:#fef3c7;color:#92400e}
.pill-bad{background:#fee2e2;color:#991b1b}
.pill-info{background:#dbeafe;color:#1e40af}
.pill-indigo{background:#e0e7ff;color:#3730a3}
.pill-muted{background:#f1f5f9;color:#475569}
html[data-theme="dark"] .pill-ok{background:#14331f;color:#86efac}
html[data-theme="dark"] .pill-warn{background:#3a2a14;color:#fcd34d}
html[data-theme="dark"] .pill-bad{background:#3a1b1b;color:#fca5a5}
html[data-theme="dark"] .pill-info{background:#15294a;color:#bfdbfe}
html[data-theme="dark"] .pill-indigo{background:#23244d;color:#c7d2fe}
html[data-theme="dark"] .pill-muted{background:#273449;color:#cbd5e1}

/* Dark mode: the light badge/flash tints above are bright pills on a dark card and the
   dark text on them washes out. Re-tint to a deep hue + light text so they stay legible
   AND sit on the dark surface. (Only .badge.draft had an override before.) */
html[data-theme="dark"] .badge.sent{background:#15294a;color:#bfdbfe}
html[data-theme="dark"] .badge.partially_refunded{background:#123b38;color:#99f6e4}
html[data-theme="dark"] .badge.refunded{background:#273449;color:#cbd5e1}
html[data-theme="dark"] .badge.accepted{background:#14331f;color:#86efac}
html[data-theme="dark"] .badge.declined{background:#3a1b1b;color:#fca5a5}
html[data-theme="dark"] .badge.est{background:#3a2a14;color:#fcd34d}
html[data-theme="dark"] .badge.invoiced{background:#23244d;color:#c7d2fe}
html[data-theme="dark"] .badge.partial{background:#3a2a14;color:#fcd34d}
html[data-theme="dark"] .badge.complete{background:#14331f;color:#86efac}
html[data-theme="dark"] .flash.success{background:#14331f;color:var(--ink)}
html[data-theme="dark"] .flash.error{background:#3a1b1b;color:#fca5a5}
html[data-theme="dark"] .flash.info{background:#15294a;color:#bfdbfe}
/* Theme-aware row highlights — replace hardcoded #fffbeb / #fef2f2 / #fff7ed <tr>
   backgrounds that left --ink (light in dark mode) text invisible on a near-white row.
   Translucent tints read correctly on BOTH the light (#fff) and dark (#172234) card. */
tr.row-warn, .row-warn{background:rgba(217,119,6,.13)}
tr.row-danger, .row-danger{background:rgba(220,38,38,.13)}
tr.row-info, .row-info{background:rgba(37,99,235,.12)}

/* Tables */
.table-wrap{width:100%;max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}
.table-wrap table{min-width:600px}
/* PO/Reece order line tables have enough fixed columns that the global 600px
   floor makes descriptions unreadably narrow. Give the name column room and let
   narrow viewports scroll horizontally instead of breaking words. */
.card .po-lines-table,
.card .reece-preview-table,
.card .reece-cart-table{max-width:none;table-layout:auto;overflow-wrap:normal;word-break:normal}
.card .po-lines-table{min-width:900px}
.card .reece-preview-table{min-width:860px}
.card .reece-cart-table{min-width:980px}
.po-lines-table th,.po-lines-table td,
.reece-preview-table th,.reece-preview-table td,
.reece-cart-table th,.reece-cart-table td{overflow-wrap:normal;word-break:normal;vertical-align:top}
.po-line-desc{min-width:380px;width:44%;text-align:left;white-space:normal}
.po-line-qty{min-width:116px;text-align:center;white-space:nowrap}
.po-line-unit{min-width:82px;text-align:center;white-space:nowrap}
.po-line-received{min-width:112px;text-align:center;white-space:nowrap}
.po-line-money{min-width:116px;text-align:right;white-space:nowrap}
.po-line-total-label{text-align:right;white-space:nowrap}
.reece-line-desc{min-width:340px;width:38%;text-align:left;white-space:normal}
.reece-qty-col{min-width:96px;text-align:center;white-space:nowrap}
.reece-unit-col{min-width:86px;text-align:center;white-space:nowrap}
.reece-input-col{min-width:118px;text-align:center;white-space:nowrap}
.reece-status-col{min-width:150px;text-align:left;white-space:normal}
.reece-money-col{min-width:120px;text-align:right;white-space:nowrap}
.reece-action-col{min-width:68px;text-align:right;white-space:nowrap}
.po-line-desc .hint,
.reece-line-desc .hint{white-space:nowrap}
/* Job/title column: a sensible width floor so long job descriptions get room
   instead of wrapping into many lines while the fixed date/status columns hog
   the row. The table scrolls horizontally, so widening this is safe. */
.col-job{min-width:260px}
/* Quote/invoice number column: never wrap the reference (e.g. "Q-1001"). */
.col-id{white-space:nowrap}
/* Promo codes table (8 cols): keep headings + short cells on one line so the
   table scrolls instead of squishing every heading into a narrow stack. */
.promo-table{min-width:780px}
.promo-table th{white-space:nowrap}
/* "Verify" links under a market-priced line: where the suggested price came from. */
.li-verify{display:block;font-size:11px;color:var(--muted);margin-top:3px;line-height:1.5}
.li-verify a{color:var(--teal)}
.compare tbody tr:nth-child(even){background:var(--bg)}
.compare tbody td{vertical-align:middle}
.compare th{vertical-align:bottom}

/* CSS bar charts (team analytics) — dependency-free, accurate heights because
   the bar % is relative to .bar-track (flex:1), with labels sitting outside it. */
.chart{display:flex;align-items:stretch;gap:8px;height:200px;overflow-x:auto;padding-bottom:2px}
.bar-col{flex:1 0 36px;display:flex;flex-direction:column;min-width:36px}
.bar-track{flex:1;display:flex;align-items:flex-end;justify-content:center;gap:0}
.bar{width:62%;background:var(--teal);border-radius:6px 6px 0 0;min-height:2px;transition:height .2s}
.bar:hover{filter:brightness(1.08)}
.bar-val{text-align:center;font-size:11px;font-weight:700;margin-top:5px}
.bar-lbl{text-align:center;font-size:11px;color:var(--muted);white-space:nowrap}
table{width:100%;border-collapse:collapse}
th,td{text-align:left;padding:11px 10px;border-bottom:1px solid var(--line);font-size:14px}
th{font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
/* Headings are short labels — inside a scroll wrapper never let them break into
   squished multi-line stacks on mobile; the table scrolls sideways instead.
   Scoped to .table-wrap so bare (narrow) tables can't overflow the page.
   Long free-text CELLS (job titles, descriptions) still wrap. */
.table-wrap th{white-space:nowrap}
/* Money/number cells stay on one line so amounts never split. */
td.num,th.num{text-align:right;white-space:nowrap}
/* Phones: titles truncate so the MONEY (total + status) stays on screen — a
   horizontally-scrolled-away total is an invisible total. */
@media(max-width:640px){
  td.col-job,th.col-job{min-width:0;max-width:26vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  th,td{padding:11px 6px}
}
tr:last-child td{border-bottom:none}

/* ===== Stat / KPI tiles — matched to the app's KpiCard (multicoloured) =====
   Each tile carries its own accent (data-accent) used on the value + the round
   icon chip, exactly like the RN dashboard. brandSoft-style chip background,
   20px radius card, accent-coloured number. Light + dark parity below. */
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
@media(max-width:780px){.stats{grid-template-columns:1fr 1fr}}
.stat{position:relative;background:var(--card);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:16px 16px 15px;overflow:hidden;transition:border-color .15s,box-shadow .15s,background .15s}
.stat .stat-top{display:flex;align-items:flex-start;gap:8px;min-height:26px}
.stat .l{flex:1;min-width:0;font-size:11px;font-weight:700;letter-spacing:.03em;color:var(--muted);
  line-height:1.35;padding-right:2px}
/* Round icon chip (app: 26px circle, brandSoft bg, accent-coloured glyph). */
.stat .stat-chip{flex:0 0 auto;width:30px;height:30px;border-radius:50%;display:inline-flex;
  align-items:center;justify-content:center;color:var(--stat-accent,var(--teal));
  background:var(--stat-soft,var(--teal-light))}
.stat .stat-chip .ic{display:block}
.stat .v{font-size:27px;font-weight:800;letter-spacing:-.5px;line-height:1.1;
  color:var(--ink)}
/* App-style spacing only when the tile has the new header row (label + chip).
   Legacy .stat tiles (team_analytics: .v then .l, no .stat-top) keep their own flow. */
.stat .stat-top+.v{margin-top:12px}
.stat .v+.l{margin-top:5px}
/* Metric picker kebab — tucked beside the chip, quiet until hover. */
.stat .stat-kebab{flex:0 0 auto;background:none;border:0;cursor:pointer;color:var(--muted);
  font-size:18px;line-height:1;padding:0 2px;margin:-2px -4px 0 0;opacity:.55;transition:opacity .12s}
.stat:hover .stat-kebab{opacity:1}
.stat .dash-pick{position:absolute;top:42px;right:10px;z-index:30;background:var(--card);
  border:1px solid var(--line);border-radius:10px;box-shadow:0 8px 22px rgba(0,0,0,.16);padding:6px}
.stat:hover{border-color:var(--stat-accent,var(--teal));
  box-shadow:0 6px 18px color-mix(in srgb,var(--stat-accent,var(--teal)) 16%,transparent)}
/* Accent palette — the app's exact KpiCard tile colours (light mode). */
.stat[data-accent="brand"]{--stat-accent:var(--teal);--stat-soft:var(--teal-light)}
.stat[data-accent="green"]{--stat-accent:#16a34a;--stat-soft:rgba(22,163,74,.10)}
.stat[data-accent="amber"]{--stat-accent:#d97706;--stat-soft:rgba(217,119,6,.10)}
.stat[data-accent="blue"]{--stat-accent:#2563eb;--stat-soft:rgba(37,99,235,.10)}
.stat[data-accent="violet"]{--stat-accent:#7c3aed;--stat-soft:rgba(124,58,237,.10)}
@media(max-width:380px){.stat .v{font-size:24px}}
/* Dark mode: brighten accents to the app's dark palette + softer chip fills. */
html[data-theme="dark"] .stat[data-accent="brand"]{--stat-accent:#2EA98C;--stat-soft:rgba(46,169,140,.16)}
html[data-theme="dark"] .stat[data-accent="green"]{--stat-accent:#22c55e;--stat-soft:rgba(34,197,94,.16)}
html[data-theme="dark"] .stat[data-accent="amber"]{--stat-accent:#fbbf24;--stat-soft:rgba(251,191,36,.16)}
html[data-theme="dark"] .stat[data-accent="blue"]{--stat-accent:#60a5fa;--stat-soft:rgba(96,165,250,.16)}
html[data-theme="dark"] .stat[data-accent="violet"]{--stat-accent:#c084fc;--stat-soft:rgba(192,132,252,.16)}

/* Landing */
.hero{padding:72px 0 48px;text-align:center}
.hero h1{font-size:48px;letter-spacing:-.02em;margin-bottom:18px}
.hero p.sub{font-size:20px;color:var(--slate);max-width:620px;margin:0 auto 28px}
.hero .cta{display:flex;gap:14px;justify-content:center;flex-wrap:wrap}
@media(max-width:640px){.hero h1{font-size:34px}.hero p.sub{font-size:17px}}
.kicker{color:var(--muted);font-weight:800;letter-spacing:.06em;text-transform:uppercase;font-size:13px;margin-bottom:14px}
.section{padding:48px 0}
.feature{padding:22px}
.feature .ico{width:44px;height:44px;border-radius:12px;background:var(--teal-light);color:var(--teal-dark);
  display:flex;align-items:center;justify-content:center;font-size:22px;margin-bottom:14px}
.feature .ico svg{width:22px;height:22px}
.feature h3{font-size:18px}
.feature p{color:var(--slate);font-size:15px;margin:0}
.banner{background:linear-gradient(135deg,var(--teal),var(--teal-dark));color:#fff;border-radius:18px;
  padding:40px;text-align:center;margin:20px 0}
.banner h2{color:#fff;font-size:28px}
.banner .btn{background:var(--card);color:var(--teal-dark)}
.banner .btn:hover{background:#e9f5f2}
.promo-code{background:var(--card);color:#118173;padding:1px 8px;border-radius:6px;letter-spacing:1px}
.logos{display:flex;gap:14px 26px;justify-content:center;flex-wrap:wrap;color:var(--muted);font-weight:700;
  font-size:12px;letter-spacing:.08em;text-transform:uppercase;margin-top:18px}

/* Rotating integrations banner: a seamless auto-scrolling marquee. The track holds
   the chips TWICE and slides left by 50%, so it loops with no visible jump. */
.marquee{overflow:hidden;width:100%;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent)}
.marquee-track{display:flex;gap:30px;width:max-content;animation:marquee 28s linear infinite}
.marquee:hover .marquee-track{animation-play-state:paused}
.intg{display:inline-flex;align-items:center;gap:9px;white-space:nowrap;font-weight:700;font-size:18px;
  letter-spacing:-.01em;color:var(--slate);background:var(--card);border:1px solid var(--line);
  border-radius:12px;padding:11px 20px;box-shadow:0 1px 2px rgba(15,23,42,.05)}
/* Brand chips: official colours + a brand-coloured dot, so they read as a logo
   lockup without shipping the trademarked artwork. */
.intg-xero{color:#1AB4D7;border-color:#1AB4D733}
.intg-myob{color:#6300A5;border-color:#6300A533}
.intg-qbo{color:#2CA01C;border-color:#2CA01C33}
.intg-stripe{color:#635BFF;border-color:#635BFF33}
.intg-xero::before,.intg-myob::before,.intg-qbo::before,.intg-stripe::before{
  content:"";width:9px;height:9px;border-radius:50%;background:currentColor;flex:none}
/* Real brand logos: uniform height on a white chip so every brand's own colours
   read correctly regardless of the source artwork's aspect ratio. */
.intg-logo{padding:12px 24px}
.intg-logo img{height:26px;width:auto;display:block}
@keyframes marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media(prefers-reduced-motion:reduce){
  .marquee{-webkit-mask-image:none;mask-image:none}
  .marquee-track{animation:none;flex-wrap:wrap;justify-content:center;width:auto;gap:14px}
}

/* Pricing */
.plans{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:18px;align-items:start}
@media(max-width:560px){.plans{grid-template-columns:1fr}}
.plan{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:26px;position:relative}
.plan.featured{border:2px solid var(--teal);box-shadow:var(--shadow)}
.plan .price{font-size:40px;font-weight:800;margin:8px 0}
.plan .price span{font-size:16px;color:var(--muted);font-weight:600}
.plan ul{list-style:none;padding:0;margin:18px 0}
.plan li{padding:7px 0 7px 26px;position:relative;color:var(--slate);font-size:15px}
.plan li:before{content:"✓";position:absolute;left:0;color:var(--green);font-weight:800}
.tag{position:absolute;top:-12px;right:20px;background:var(--teal);color:#fff;font-size:12px;
  font-weight:700;padding:4px 12px;border-radius:999px}

/* Misc */
.muted{color:var(--muted)}
.center{text-align:center}
.mt{margin-top:18px}.mt2{margin-top:32px}
.flex{display:flex;align-items:center;gap:12px}
.flex.between{justify-content:space-between;flex-wrap:wrap}
.tag-trade{font-size:13px;color:var(--slate)}
.empty{text-align:center;padding:48px 20px;color:var(--muted)}
.empty .big{font-size:48px;margin-bottom:8px}
/* Stacked line-item rows: description full-width on top, controls below — far
   clearer than the old cramped table (description was truncated, the type
   dropdown was squashed) and it wraps cleanly on mobile. */
/* Light two-line rows: description on its own line, then Type/Qty/Unit/Unit$/Total
   inline beneath. Just a separator between lines — no heavy boxes. */
/* Group headers on the review screen (Labour / Materials / Other). */
.li-group-head{font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;
  color:var(--muted);margin:16px 0 4px;padding-left:2px}
.li-group-head:first-child{margin-top:2px}
/* Left status bar is a real border (reserves space) + a left gutter, so the
   description/controls inputs sit beside it and never overlap it. */
.li-row{padding:10px 4px 10px 12px;border-bottom:1px solid var(--line);border-left:3px solid transparent}
.li-row.review{background:#fffbeb}
.li-row.saved:not(.review){border-left-color:var(--green)}
.li-desc{width:100%;font-size:15px;padding:8px 10px}
.li-badge{margin-top:5px}
.li-badge:empty{display:none}
/* Line 2: Type/Qty/Unit/Unit$/Total stay on ONE line (never wrap to 3+ lines);
   on a very narrow phone this row scrolls horizontally on its own rather than
   widening the page. */
.li-controls{display:flex;flex-wrap:nowrap;align-items:center;gap:8px;margin-top:8px;overflow-x:auto;padding-bottom:3px}
.li-controls select{flex:none;min-height:34px;height:34px;padding:6px 7px;line-height:1.35;font-size:13px;width:auto}
.li-ctl{flex:none;font-size:11px;color:var(--muted);display:inline-flex;align-items:center;gap:4px}
.li-ctl input{flex:none;min-height:34px;height:34px;padding:6px 7px;line-height:1.35;font-size:13px;width:58px}
.li-ctl input.unit{width:50px}
.li-ctl input.price{width:92px}   /* Unit $ — room for larger dollar amounts */
.li-total{flex:none;margin-left:auto;font-size:14px;white-space:nowrap;padding-left:6px}
/* Expanding a line keeps the clean top line unchanged and opens the .li-detail tray beneath it
   (styled in quote_view.html's inline block). No hardcoded expanded background — the old #fffdf5
   cream broke dark mode and read as bland grey-on-grey. */
.li-detail .li-ctl{color:var(--ink)}
.li-detail .li-ctl input{max-width:120px}
.notice{background:var(--teal-light);border:1px solid var(--teal);color:var(--ink);
  padding:12px 16px;border-radius:10px;font-size:14px}
/* Combined upload dropbox */
.dropbox{display:flex;flex-direction:column;align-items:center;gap:2px;text-align:center;cursor:pointer;
  border:2px dashed var(--line);border-radius:12px;background:var(--bg);padding:20px 16px;transition:.15s}
.dropbox:hover,.dropbox.drag{border-color:var(--teal);background:var(--teal-light)}
.spinner{display:none;align-items:center;gap:10px;color:var(--slate);font-weight:600;margin-top:14px}
.spinner.on{display:flex}
.dots{width:18px;height:18px;border:3px solid var(--teal-light);border-top-color:var(--teal);
  border-radius:50%;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

/* --- notifications bell --- */
.bell-wrap{position:relative;display:inline-flex}
.bell-btn{position:relative;background:none;border:0;cursor:pointer;color:var(--muted);padding:6px;border-radius:8px;display:inline-flex;align-items:center}
.bell-btn:hover{color:var(--teal);background:color-mix(in srgb,var(--teal) 12%,transparent)}
.bell-badge{position:absolute;top:0;right:0;min-width:16px;height:16px;padding:0 4px;border-radius:9px;background:#e11d48;color:#fff;font-size:11px;font-weight:700;line-height:16px;text-align:center}
.bell-panel{position:absolute;top:calc(100% + 8px);right:0;width:340px;max-width:86vw;background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:0 12px 32px rgba(2,6,23,.18);z-index:60;overflow:hidden}
@media(max-width:720px){.bell-panel{position:fixed;top:62px;left:10px;right:10px;width:auto;max-width:none}}
.bell-head{display:flex;justify-content:space-between;align-items:center;padding:12px 14px;border-bottom:1px solid var(--line)}
.linkbtn{background:none;border:0;color:var(--teal);font-weight:600;cursor:pointer;font-size:13px}
.bell-items{max-height:60vh;overflow-y:auto}
.bell-item{display:block;padding:11px 14px;border-bottom:1px solid var(--line);text-decoration:none;color:var(--ink)}
.bell-item:hover{background:var(--bg)}
.bell-item.unread{background:color-mix(in srgb,var(--teal) 8%,transparent)}
.bell-item.unread .bi-title{font-weight:700}
.bi-title{font-size:14px}
.bi-body{font-size:13px;color:var(--muted);margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bi-ago{font-size:11px;color:var(--muted);margin-top:3px}
.bell-foot{display:block;text-align:center;padding:10px;font-size:13px;font-weight:600;color:var(--teal);text-decoration:none}
.bell-foot:hover{background:var(--bg)}
.bell-empty{padding:20px 14px;color:var(--muted);text-align:center;font-size:14px}
/* unread bubble on dashboard quote rows */
.qbubble{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:#e11d48;color:#fff;font-size:11px;font-weight:700;vertical-align:middle}

/* Narrow screens: let the nav wrap instead of widening the whole page */
/* Keep the nav on ONE line on phones: shrink the gap and drop the wordmark
   (logo stays) so every icon fits without squishing or wrapping. */
@media(max-width:600px){.nav .container{gap:9px;flex-wrap:nowrap}.nav .brand .wordmark{display:none}}
@media(max-width:430px){.nav .container{gap:7px}.acct-name{display:none}}

/* ── Audit tidy-up (styling only; no theme change) ─────────────────────────
   Opt-in wrapping for long button labels (the .btn default is white-space:nowrap,
   which overflows on narrow phones for long single-word labels). */
.btn.wrap{white-space:normal;text-align:center;line-height:1.25}
/* Cap line-item number inputs so the fixed pixel widths never overflow their row. */
.li-ctl input{max-width:100%}
/* Wide tables nested inside a card (not just direct children) get a horizontal
   scroller on small screens instead of overflowing/clipping. */
@media(max-width:720px){
  .card table:not(.table-wrap table){display:block;overflow-x:auto;-webkit-overflow-scrolling:touch}
}
/* Four-up stat tiles go single-column on the narrowest phones so big numbers
   don't crowd/clip. */
@media(max-width:380px){.stats{grid-template-columns:1fr}}

/* ── App-parity polish (allquote-rn) ───────────────────────────────────────
   Purely cohesive/visual: makes web cards, buttons, nav and focus rings read
   like the mobile app — soft 14px corners, thin borders, bright teal accent,
   subtle press feedback. No layout/overflow/table-alignment changes. Every
   rule reuses the existing CSS variables so light/dark parity is automatic. */

/* Cards: the app gives surfaces a touch more lift + a slightly tighter hairline
   border. GlassCard.js uses a 20px corner (--radius-lg); the smooth transition is
   for the hover lift. (The mobile block below trims back to --radius for the
   tighter phone shell.) */
.card{border-radius:var(--radius-lg);transition:border-color .15s,box-shadow .15s}

/* Buttons: app uses a 10px corner + a soft active-press dip. The primary teal
   gains a faint brand-tinted glow so the CTA "pops" like the app's FAB/buttons. */
.btn{border-radius:var(--radius-sm)}
.btn:active{transform:translateY(.5px)}
.btn:not(.ghost):not(.danger){box-shadow:0 1px 2px color-mix(in srgb,var(--teal) 35%,transparent)}
html[data-theme="dark"] .btn:not(.ghost):not(.danger){
  box-shadow:0 1px 14px color-mix(in srgb,var(--teal) 28%,transparent)}
@media(prefers-reduced-motion:reduce){.btn:active{transform:none}}

/* Inputs/select/textarea: app rounds to 10px. (Spec radius scale 14/10/7.) */
input:not([type]),input[type=text],input[type=email],input[type=password],input[type=number],
input[type=color],input[type=tel],input[type=date],input[type=time],input[type=datetime-local],
input[type=search],input[type=url],input[type=file],select,textarea{border-radius:var(--radius-sm)}

/* Focus ring: the app's signature teal halo. In dark mode --teal-light is a dim
   surface (not a glow), so paint the halo from the bright --teal at low alpha. */
html[data-theme="dark"] input:focus,
html[data-theme="dark"] select:focus,
html[data-theme="dark"] textarea:focus{
  box-shadow:0 0 0 3px color-mix(in srgb,var(--teal) 22%,transparent)}

/* Nav: thinner, app-like sticky bar shadow once you scroll under it. */
.nav{box-shadow:0 1px 0 var(--line)}

/* Pills/badges already use 999px (app circular indicators) — leave as-is.
   Nav burger + small chips match the app's 7px tighter radius. */
.nav-burger{border-radius:var(--radius-xs)}

/* Brand wordmark stays branded even though ordinary --ink is neutral text. */
.brand .wordmark .w1{color:var(--aq-wordmark-all)}

/* Feature/icon tiles + dropbox: round to the app's 14px card corner so the
   landing page reads like the app's onboarding cards. */
.feature .ico{border-radius:var(--radius)}
.dropbox{border-radius:var(--radius)}

/* ── Mobile app-parity (allquote-rn) ───────────────────────────────────────
   Tighten the MOBILE web shell so it reads like the RN app (the app loads
   allquote.com.au in a webview). Mirrors the app's Header (components/Header.js)
   and its tidy dark card rhythm. Bottom-nav + side-drawer styling live inline in
   base.html; this block covers the TOP bar + content spacing on small screens.
   Desktop (>720px) is untouched; every colour comes from the theme tokens so
   light/dark parity is automatic. */
@media(max-width:720px){
  /* Top bar → the app's Header: a tidy 56px row, the brand mark left, action
     icons (messages/bell/☰) clustered right with even breathing room. */
  .nav .container{height:56px;gap:10px}
  .nav a.navlink{padding:6px}
  /* The icon-only action links in the bar (messages, bell, new, dashboard) get an
     even, app-like 34px tap target so the right cluster lines up like Header.js. */
  .nav .container>a.navlink,
  .nav .container>.bell-wrap>.bell-btn{display:inline-flex;align-items:center;justify-content:center}
  /* Account ☰ button reads as the app's menu button (square, subtle, teal on press). */
  .acct-btn{border-color:transparent;background:none}
  .acct-btn:active{background:var(--teal-light);color:var(--teal)}
  /* Content: the app sits cards on a tinted page with a little side gutter rather
     than edge-to-edge, so they read as discrete surfaces like the RN screens. */
  main.container{padding-left:14px;padding-right:14px}
  .card{border-radius:var(--radius)}
  /* Section headings on mobile match the app's compact screen titles. */
  main.container h1{font-size:22px}
  main.container h2{font-size:19px}
}

/* Job list tables: fixed operational columns, flexible ellipsis job name. */
.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}
.aq-job-table-wrap{width:100%;position:relative}
.aq-job-table{width:100%;border-collapse:collapse;table-layout:fixed;font-size:13px}
.aq-job-table th,.aq-job-table td{
  padding:8px 9px;border-top:1px solid var(--line);text-align:left;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:middle
}
.aq-job-table thead th{border-top:0;border-bottom:1px solid var(--line);color:var(--muted);font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;background:var(--bg)}
.aq-job-table .aq-job-col-num{width:94px}
.aq-job-table .aq-job-col-status{width:118px}
.aq-job-table .aq-job-col-date{width:108px}
.aq-job-table .aq-job-col-hours{width:96px}
.aq-job-table .aq-job-col-actions{width:42px}
.aq-job-num a{color:var(--muted);font-weight:800;text-decoration:none}
.aq-job-name-cell{min-width:0}
.aq-job-name-line{display:flex;align-items:center;gap:6px;min-width:0}
.aq-job-name-wrap{display:flex;align-items:center;gap:9px;min-width:0}
.aq-job-icon{width:32px;height:32px;border-radius:8px;border:1px solid var(--line);background:var(--brand-soft);color:var(--teal);display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:850;overflow:hidden;flex:0 0 32px}
.aq-job-icon img{width:100%;height:100%;display:block;object-fit:cover}
.aq-job-name{display:block;flex:1 1 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:inherit;text-decoration:none;font-weight:750}
.aq-job-name:focus-visible,.aq-job-num a:focus-visible,.job-kebab-btn:focus-visible{outline:2px solid var(--teal);outline-offset:2px;border-radius:6px}
.aq-job-status .badge{max-width:100%;overflow:hidden;text-overflow:ellipsis}
.aq-job-actions{overflow:visible!important;text-align:right}
.job-kebab{position:relative;display:inline-flex;justify-content:flex-end}
.job-kebab-btn{width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line);border-radius:8px;background:var(--card);color:var(--muted);cursor:pointer;padding:0}
.job-kebab-btn[aria-expanded="true"],.job-kebab-btn:hover{border-color:var(--teal);color:var(--teal);background:var(--teal-light)}
.job-kebab-menu{position:absolute;right:0;top:calc(100% + 5px);z-index:70;min-width:164px;border:1px solid var(--line);border-radius:8px;background:var(--card);box-shadow:0 12px 28px rgba(2,6,23,.18);padding:5px}
.job-kebab-menu form{margin:0}
.job-kebab-menu button{width:100%;display:flex;align-items:center;gap:8px;border:0;background:transparent;color:var(--ink);font:inherit;font-size:13px;font-weight:700;text-align:left;border-radius:6px;padding:8px 9px;cursor:pointer;white-space:nowrap}
.job-kebab-menu button:hover,.job-kebab-menu button:focus{background:var(--bg)}
.job-kebab-menu button.danger{color:#dc2626}
@media(max-width:680px){
  .aq-job-table .aq-job-hours,.aq-job-table .aq-job-col-hours{display:none}
  .aq-job-table .aq-job-col-num{width:82px}
  .aq-job-table .aq-job-col-status{width:104px}
  .aq-job-table .aq-job-col-date{width:94px}
}
[data-file-dropzone]{transition:background-color .15s ease,box-shadow .15s ease}
[data-file-dropzone].is-file-dragging{background:var(--brand-soft);box-shadow:0 0 0 2px var(--teal)}
