/* ============================================================================
   CONSOLE DESIGN TOKENS  (visual pass, Sept 2026)

   This file is loaded by app/index.html and by nothing else, so a :root block
   here is scoped to the console in practice: the marketing site, the public
   quote/document pages and the customer portal never see it. That is why the
   whole palette can be redefined here rather than in assets/site.css or
   assets/app.css, both of which are shared with those pages.

   Two things follow from that:
   1. The legacy aliases (--signal, --paper, --line ...) are redefined rather
      than removed, so every rule already written against them moves to the new
      palette without being touched.
   2. Nothing outside /app/ changes. See console-visual-redesign-plan-sept-2026.

   Brand: purple #6C3C90 is the accent, the teal is secondary. Colour never
   carries meaning on its own - every status also has a word, and usually a
   shape, so the console stays readable without it.
   ========================================================================= */
:root {
  /* ---- surfaces. The page is a quiet tinted grey; cards are white and sit
         on top of it. That contrast is what was missing before: everything
         was the same white, so nothing read as grouped. */
  --bg: #F4F3F7;
  --surface: #FFFFFF;
  --surface-2: #F4F2F8;      /* subtle fill: table heads, muted badges */
  --surface-3: #EDEAF3;      /* hover fill */
  --paper: #FFFFFF;          /* input and control ground - stays white */
  --line: #E6E3EC;           /* hairline: card borders, dividers */
  --line-2: #918AA1;         /* control borders - 3.30:1 on white, meets 1.4.11 */

  /* ---- ink */
  --ink: #17141F;            /* 16.6:1 */
  --ink-2: #4B4557;          /*  8.5:1 */
  --ink-3: #6E6780;          /*  5.3:1 - meta text, still passes AA */

  /* ---- brand */
  --accent: #6C3C90;         /* 7.8:1 as text on white, and white on it */
  --accent-hover: #563073;
  --accent-press: #46285E;
  --accent-tint: #F3EEF8;
  --accent-tint-2: #E7DDF1;
  --teal: #0E4F4D;           /* secondary: scheduled, calendar, info accents */
  --teal-hover: #0A3B39;
  --teal-tint: #E7F1F0;

  /* ---- status. One colour per meaning, used by badges, stage pills and the
         stepper alike, so a colour means the same thing on every screen. */
  --ok: #1F6B3A;      --ok-tint: #E8F3EC;      /* customer, complete, paid */
  --info: #1D4E8F;    --info-tint: #E9F1FA;    /* quoted, sent, in progress */
  --amber: #8A4B0A;   --amber-tint: #FBF0E3;   /* lead, needs attention */
  --money: #B54708;   --money-tint: #FDF1E8;   /* payment owed */
  --stop: #B42318;    --stop-tint: #FCECEA;    /* lost, declined, failed */

  /* ---- legacy aliases. Every rule written before this pass points at these,
         so redefining them here moves the whole console at once. */
  --signal: var(--accent);
  --signal-on: #FFFFFF;
  --signal-soft: var(--accent-tint);
  --brand: var(--teal);
  --brand-hover: var(--teal-hover);
  --brand-tint: var(--teal-tint);
  --success: var(--ok);
  --success-soft: var(--ok-tint);
  --warn: var(--amber);
  --warn-soft: var(--amber-tint);
  --danger: var(--stop);
  --danger-soft: var(--stop-tint);
  --money-soft: var(--money-tint);

  /* ---- radii. Rounder than the old 3px, which read as unfinished. */
  --r-xs: 4px; --r-sm: 6px; --r: 8px; --r-md: 10px; --r-lg: 12px; --r-xl: 16px;
  --r-pill: 999px;

  /* ---- elevation. Soft and low: this is a tool, not a landing page. */
  --sh-sm: 0 1px 2px rgb(20 16 32 / .06);
  --sh-md: 0 4px 12px -4px rgb(20 16 32 / .12), 0 1px 2px rgb(20 16 32 / .05);
  --sh-lg: 0 18px 44px -18px rgb(20 16 32 / .30), 0 2px 6px rgb(20 16 32 / .07);
  --ring: 0 0 0 3px rgb(108 60 144 / .20);

  /* ---- type. One sans for everything. Monospace is reserved for things that
         are genuinely code-like: record ids, tokens, keyboard keys. An email
         address is not code, and neither is a status word. */
  --f-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --f-display: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --f-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --t-title: 22px;     /* page title, 600 */
  --t-card: 15px;      /* card and section heading, 600 */
  --t-body: 14px;
  --t-meta: 13px;
  --t-small: 12px;

  /* ---- spacing and layout */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 20px;
  --sp-6: 24px; --sp-8: 32px; --sp-10: 40px;
  --sidebar-w: 232px;
  --topbar-h: 58px;
  --content-max: 1280px;
  --control-h: 36px;
}

/* ---- base ---------------------------------------------------------------
   Scoped to body.app so nothing here can leak into a page that happens to
   load this file by accident. */
body.app { background: var(--bg); color: var(--ink); font-family: var(--f-ui);
  font-size: var(--t-body); line-height: 1.5; -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale; margin: 0; }
body.app *, body.app *::before, body.app *::after { box-sizing: border-box; }
body.app h1, body.app h2, body.app h3, body.app h4 { font-family: var(--f-display);
  letter-spacing: -.011em; }
body.app :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }


/* Console styles that belong to the console alone.

   Load order is site.css (colour tokens, fonts) -> assets/app.css (the CRM
   base: shell, rows, panels, status pills, the job stepper) -> this file.
   assets/app.css is shared with the public quote, document and portal pages,
   so anything only the console needs lives here instead of being added there.

   Everything above the "shared list components" rule was lifted verbatim out
   of the <style> block that used to sit inside app/index.html, so the split
   into modules changed no pixels. */

/* Conversation threads (Inbox + contact page). Same language as the portal. */
.thread { display: flex; flex-direction: column; gap: 10px; margin: 14px 0 18px; }
.bubble { border: 1px solid var(--line); background: var(--surface); border-radius: 3px;
  padding: 12px 14px; max-width: 85%; }
.bubble.me { align-self: flex-end; background: var(--signal-soft); border-color: var(--signal); }
.bubble .who { font-family: "IBM Plex Mono", monospace; font-size: 10.5px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 4px; }
.bubble.me .who { color: var(--signal); }
.bubble p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.authtoggle { display: flex; gap: 6px; margin-bottom: 14px; }
.authtoggle button { flex: 1; }
.acts a.btn { text-decoration: none; }


/* ---------------------------------------------------- shared list components
   The toolbar, sub-tab chips and combobox are defined here in the phase that
   splits the console into modules, ahead of the phase that puts them on
   screen. Nothing renders them yet, so none of this is reachable CSS today. */

/* Toolbar: search box on the left, sort on the right, chips underneath. */
.toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
.toolbar-search { position: relative; flex: 1 1 260px; min-width: 200px; }
.toolbar-search input { font: inherit; font-size: 14px; color: var(--ink); background: var(--paper);
  border: 1px solid var(--line-2); border-radius: 3px; padding: 8px 30px 8px 10px; width: 100%; }
.toolbar-search input:focus { outline: 0; border-color: var(--signal);
  box-shadow: 0 0 0 3px var(--signal-soft); }
.toolbar-search input::-webkit-search-cancel-button { display: none; }
.tb-clear { position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; color: var(--ink-3); font-size: 18px; line-height: 1;
  cursor: pointer; padding: 2px 6px; }
.tb-clear:hover { color: var(--ink); }
.tb-sort { font: inherit; font-size: 13px; color: var(--ink-2); background: var(--paper);
  border: 1px solid var(--line-2); border-radius: 3px; padding: 8px 10px; flex: none; }

/* Sub-tabs. Horizontally scrollable, because on a phone there are eight job
   stages and they must not wrap into a wall. */
.subtabs { display: flex; gap: 6px; margin-bottom: 14px; overflow-x: auto;
  scrollbar-width: none; padding-bottom: 2px; }
.subtabs::-webkit-scrollbar { display: none; }
.subtab { background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 13px; font: inherit; font-size: 13px; color: var(--ink-2); cursor: pointer;
  white-space: nowrap; flex: none; display: flex; align-items: center; gap: 7px; }
.subtab:hover { border-color: var(--line-2); color: var(--ink); }
.subtab.on { background: var(--signal); border-color: var(--signal); color: var(--signal-on);
  font-weight: 600; }
.subtab .n { font-family: "IBM Plex Mono", monospace; font-size: 11px; font-variant-numeric: tabular-nums;
  color: var(--ink-3); }
.subtab.on .n { color: var(--signal-on); opacity: .85; }

/* Highlighted search matches. The browser default is a yellow marker pen,
   which fights every other colour in here. */
mark { background: var(--signal-soft); color: inherit; border-radius: 2px; padding: 0 1px; }

/* Combobox: a text input that filters a list, replacing a <select> that has
   grown too long to scroll. */
.combo { position: relative; }
.combo-input { font: inherit; font-size: 14px; color: var(--ink); background: var(--paper);
  border: 1px solid var(--line-2); border-radius: 3px; padding: 9px 10px; width: 100%; }
.combo-input:focus { outline: 0; border-color: var(--signal); box-shadow: 0 0 0 3px var(--signal-soft); }
.combo-list { position: absolute; z-index: 40; top: calc(100% + 3px); left: 0; right: 0;
  max-height: 260px; overflow-y: auto; background: var(--surface); border: 1px solid var(--line-2);
  border-radius: 3px; box-shadow: var(--sh-md); }
.combo-opt { display: block; width: 100%; text-align: left; background: none; border: 0;
  border-bottom: 1px solid var(--line); padding: 9px 11px; font: inherit; font-size: 13.5px;
  color: var(--ink); cursor: pointer; }
.combo-opt:last-child { border-bottom: 0; }
.combo-opt:hover, .combo-opt.on { background: var(--signal-soft); }
.combo-opt .hint { display: block; font-family: "IBM Plex Mono", monospace; font-size: 11px;
  color: var(--ink-3); margin-top: 2px; }
.combo-none { padding: 11px; font-size: 13.5px; color: var(--ink-3); }


/* ----------------------------------------------------------------- settings
   A left rail of sections, each its own page at its own URL. Replaces the
   single scrolling page of eight panels behind one Save button. */
.settings-layout { display: grid; grid-template-columns: 226px minmax(0, 1fr); gap: 22px;
  align-items: start; }
.settings-body { min-width: 0; }
.settings-body .vhead { margin-bottom: 12px; }

.rail { position: sticky; top: 72px; display: flex; flex-direction: column; gap: 1px;
  border: 1px solid var(--line); background: var(--surface); border-radius: 3px;
  padding: 6px; }
.rail-group { font-family: "IBM Plex Mono", monospace; font-size: 10px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3);
  padding: 12px 10px 5px; }
.rail-group:first-child { padding-top: 5px; }
.rail-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 3px;
  font-size: 13.5px; color: var(--ink-2); text-decoration: none; }
.rail-item:hover { background: var(--surface-2); color: var(--ink); }
.rail-item.on { background: var(--signal-soft); color: var(--signal); font-weight: 600; }
.rail-item span:first-child { flex: 1; min-width: 0; }
/* On/off for a connection. Shape as well as colour: an unconnected service is
   a hollow ring, so it does not depend on telling two colours apart. */
.rail-dot { width: 8px; height: 8px; border-radius: 50%; flex: none;
  border: 2px solid var(--line-2); box-sizing: border-box; }
.rail-dot.on { background: var(--success); border-color: var(--success); }

.savebar { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line);
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.secret-state { margin-top: 5px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.secret-state .linkbtn { padding: 0; text-decoration: underline; }
.settings-body code { font-family: "IBM Plex Mono", monospace; font-size: .92em;
  background: var(--surface-2); padding: 1px 4px; border-radius: 2px; }

@media (max-width: 780px) {
  .settings-layout { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  /* The rail becomes a scrolling chip row rather than a tall list you have to
     scroll past to reach the section you opened. */
  .rail { position: static; flex-direction: row; overflow-x: auto; scrollbar-width: none;
    gap: 4px; }
  .rail::-webkit-scrollbar { display: none; }
  .rail-group { display: none; }
  .rail-item { white-space: nowrap; flex: none; }
}

/* A dashboard tile that links into the list it counts. Same box, plus a
   pointer and a hover lift, so it reads as clickable without shouting. */
a.tile { display: block; text-decoration: none; color: inherit; }
a.tile:hover { border-color: var(--line-2); box-shadow: var(--sh-sm); }
a.tile .k, a.tile .v { color: inherit; }
a.tile.alert .v { color: var(--signal); }

/* ------------------------------------------------------------- job detail
   The head panel carries the stepper and the one thing to do next. */
.jobhead { padding-bottom: 14px; }
.nextaction { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); }
.nextaction .sub { flex: 1 1 240px; min-width: 0; }
.nextaction a.btn { text-decoration: none; }

/* A row title that opens a detail page. Underline on hover only, so a list
   does not read as a wall of links. */
.row .t a { color: inherit; text-decoration: none; }
.row .t a:hover { color: var(--signal); text-decoration: underline; }

/* ----------------------------------------------------------- quote wizard
   Five steps across the top, one panel of fields, Back/Next underneath. The
   indicator is clickable: going back is always allowed, going forward has to
   pass the steps in between. */
.wizsteps { display: flex; gap: 4px; margin-bottom: 14px; overflow-x: auto;
  scrollbar-width: none; padding-bottom: 2px; }
.wizsteps::-webkit-scrollbar { display: none; }
.wizstep { display: flex; align-items: center; gap: 8px; flex: none; white-space: nowrap;
  background: var(--surface); border: 1px solid var(--line); border-radius: 3px;
  padding: 8px 13px; font: inherit; font-size: 13px; color: var(--ink-3); cursor: pointer; }
.wizstep:hover { border-color: var(--line-2); color: var(--ink); }
.wizstep .n { font-family: "IBM Plex Mono", monospace; font-size: 11px; font-weight: 600;
  width: 19px; height: 19px; border-radius: 50%; display: inline-flex; align-items: center;
  justify-content: center; background: var(--surface-2); color: var(--ink-3); flex: none; }
.wizstep.on { border-color: var(--signal); color: var(--ink); font-weight: 600; }
.wizstep.on .n { background: var(--signal); color: var(--signal-on); }
.wizstep.done { color: var(--ink-2); }
.wizstep.done .n { background: var(--success); color: #fff; }

/* One line item, or one material. The dashed rule keeps a wrapped row from
   blending into the next one on a narrow screen. */
.wizrow { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap;
  margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px dashed var(--line); }
.wizrow:last-child { border-bottom: 0; margin-bottom: 6px; }

.wiztotal { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line);
  font-size: 14px; }
.wiztotal b { font-family: Archivo, sans-serif; font-size: 18px; letter-spacing: -.01em;
  font-variant-numeric: tabular-nums; margin: 0 4px; }

.wiznav { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 4px; flex-wrap: wrap; }
.wiznav-right { display: flex; gap: 8px; margin-left: auto; }

/* --------------------------------------------------------- global search
   Ctrl+K, or "/" from anywhere that is not a text box. The app bar carries a
   visible way in too, because a keyboard shortcut nobody knows about is not a
   feature. */
.appsearch { margin-left: auto; display: flex; align-items: center; gap: 8px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 3px;
  padding: 6px 10px; font: inherit; font-size: 13px; color: var(--ink-3); cursor: pointer; }
.appsearch:hover { border-color: var(--line-2); color: var(--ink); }
.appsearch-i { font-size: 13px; }
.appsearch-k { font-family: "IBM Plex Mono", monospace; font-size: 10px; letter-spacing: .04em;
  border: 1px solid var(--line); border-radius: 2px; padding: 1px 5px; color: var(--ink-3); }
.appbar .who { margin-left: 12px; }

.gs-back { position: fixed; inset: 0; z-index: 100; background: rgb(28 24 21 / .34);
  display: flex; align-items: flex-start; justify-content: center; padding: 10vh 16px 16px; }
.gs-panel { width: 100%; max-width: 620px; background: var(--surface);
  border: 1px solid var(--line-2); border-radius: 6px; box-shadow: var(--sh-lg);
  overflow: hidden; display: flex; flex-direction: column; max-height: 74vh; }
#gs-q { font: inherit; font-size: 16px; color: var(--ink); background: var(--surface);
  border: 0; border-bottom: 1px solid var(--line); padding: 16px 18px; width: 100%; }
#gs-q:focus { outline: 0; }
.gs-results { overflow-y: auto; flex: 1; }
.gs-group { font-family: "IBM Plex Mono", monospace; font-size: 10px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3);
  padding: 12px 18px 5px; display: flex; gap: 8px; align-items: baseline; }
.gs-more { text-transform: none; letter-spacing: 0; font-weight: 400; }
.gs-hit { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: none; border: 0; padding: 9px 18px; font: inherit; cursor: pointer; color: var(--ink); }
.gs-hit:hover, .gs-hit.on { background: var(--signal-soft); }
.gs-hit-main { flex: 1; min-width: 0; }
.gs-hit-title { display: block; font-size: 14px; font-weight: 600; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.gs-hit-sub { display: block; font-size: 12.5px; color: var(--ink-3); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; margin-top: 1px; }
.gs-note { padding: 22px 18px; color: var(--ink-3); font-size: 13.5px; }
.gs-foot { display: flex; gap: 16px; padding: 9px 18px; border-top: 1px solid var(--line);
  background: var(--surface-2); font-size: 11.5px; color: var(--ink-3); }
.gs-foot b { font-family: "IBM Plex Mono", monospace; font-weight: 600; }

/* ------------------------------------------------------------ phone pass
   Evan uses this standing next to a customer, so on a small screen the one
   thing to do next is a bar at the bottom of the page rather than something
   to scroll for. */
@media (max-width: 700px) {
  .appsearch-t, .appsearch-k { display: none; }
  .appsearch { padding: 7px 10px; }
  main.appmain { padding-bottom: 104px; }
  .nextaction { position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
    flex-direction: column; align-items: stretch; gap: 8px; margin: 0;
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
    background: var(--surface); border-top: 1px solid var(--line);
    box-shadow: 0 -6px 18px -12px rgb(28 24 21 / .3); }
  .nextaction .sub { order: -1; flex: none; font-size: 12.5px; }
  .nextaction .btn { padding: 12px 16px; font-size: 15px; text-align: center; }
  .gs-back { padding: 0; }
  .gs-panel { max-width: none; max-height: 100vh; height: 100%; border-radius: 0; border: 0; }
  .wiznav { position: sticky; bottom: 0; background: var(--paper); padding: 10px 0;
    border-top: 1px solid var(--line); }
}

/* --------------------------------------------------- animation restraint
   The stepper's pulse is a useful "you are here" on ONE job. Twenty of them
   pulsing down a list is a nervous tic, so it is scoped to the detail page. */
.stepper .step-current .dot, .stepper .step-money .dot { animation: none; }
.jobhead .stepper .step-current .dot { animation: step-pulse 2s ease-in-out infinite; }
.jobhead .stepper .step-money .dot { animation: step-pulse-money 2s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .jobhead .stepper .step-current .dot, .jobhead .stepper .step-money .dot { animation: none; }
  .gs-panel, .gs-back { animation: none; }
}

/* The logo preview on Site settings, on a chequerboard so a transparent PNG
   reads as transparent rather than as a white box. */
.logo-preview { max-width: 260px; max-height: 110px; border: 1px solid var(--line);
  border-radius: 3px; padding: 10px; background-color: var(--surface);
  background-image: linear-gradient(45deg, var(--surface-2) 25%, transparent 25%),
    linear-gradient(-45deg, var(--surface-2) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--surface-2) 75%),
    linear-gradient(-45deg, transparent 75%, var(--surface-2) 75%);
  background-size: 14px 14px;
  background-position: 0 0, 0 7px, 7px -7px, -7px 0px; }


/* ============================================================================
   SHELL AND NAVIGATION  (step 2 of the visual pass)

   The old chrome was a text row of tabs over a 900px column of content on a
   2000px screen. It is now a fixed left sidebar, a slim top bar carrying only
   search and who you are, and a content column that is allowed to be wide.
   On a phone the sidebar is replaced by a tab bar along the bottom, which is
   where a thumb already is.

   Everything below overrides assets/app.css, which is shared with the public
   quote, document and portal pages and is deliberately left alone.
   ========================================================================= */
.app-shell { display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  align-items: start; min-height: 100vh; background: var(--bg); }
.app-col { min-width: 0; display: flex; flex-direction: column; min-height: 100vh; }

/* ---- sidebar */
.sidebar { position: sticky; top: 0; height: 100vh; z-index: 30;
  display: flex; flex-direction: column; background: var(--surface);
  border-right: 1px solid var(--line); padding: 14px 12px 14px; }
.sb-brand { display: flex; align-items: center; gap: 10px; padding: 4px 8px 18px;
  text-decoration: none; color: inherit; }
.sb-mark { width: 32px; height: 32px; border-radius: 9px; flex: none; display: block; }
.sb-brand-t b { display: block; font-size: 14px; font-weight: 600; line-height: 1.2;
  letter-spacing: -.012em; color: var(--ink); }
.sb-brand-t span { display: block; font-size: 11.5px; color: var(--ink-3); margin-top: 1px; }
.sb-nav { display: flex; flex-direction: column; gap: 2px; flex: 1; min-height: 0; overflow-y: auto; }
.sb-item { display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  background: none; border: 0; border-radius: var(--r); padding: 9px 10px; font: inherit;
  font-size: 14px; font-weight: 500; color: var(--ink-2); cursor: pointer; }
.sb-item:hover { background: var(--surface-3); color: var(--ink); }
.sb-item.on { background: var(--accent-tint); color: var(--accent); font-weight: 600; }
.sb-ico { flex: none; display: inline-flex; }
.ico { width: 19px; height: 19px; display: block; }
.sb-lbl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-item .count { flex: none; min-width: 20px; height: 20px; padding: 0 6px; text-align: center;
  border-radius: var(--r-pill); background: var(--accent); color: #fff; font-size: 11px;
  font-weight: 600; line-height: 20px; font-variant-numeric: tabular-nums; }

/* ---- top bar */
.appbar { position: sticky; top: 0; z-index: 25; display: flex; align-items: center; gap: 14px;
  height: var(--topbar-h); padding: 0 22px; background: var(--surface);
  border-bottom: 1px solid var(--line); }
.ab-mark { display: none; }
.appsearch { margin-left: 0; display: flex; align-items: center; gap: 9px; height: 34px;
  min-width: 260px; max-width: 420px; flex: 0 1 420px; text-align: left;
  background: var(--surface-2); border: 1px solid transparent; border-radius: var(--r);
  padding: 0 10px; font: inherit; font-size: 13px; color: var(--ink-3); cursor: pointer; }
.appsearch:hover { background: var(--surface-3); border-color: var(--line); color: var(--ink-2); }
.appsearch .ico { width: 16px; height: 16px; flex: none; }
.appsearch-t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.appsearch-k { flex: none; font-family: var(--f-mono); font-size: 10.5px; letter-spacing: .02em;
  border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-xs);
  padding: 2px 5px; color: var(--ink-3); }
.ab-who { margin-left: auto; display: flex; align-items: center; gap: 10px; min-width: 0; }
.ab-name { font-size: 13.5px; color: var(--ink-2); max-width: 210px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
/* The role chip was small monospace capitals, which read as a log line. */
.role { font-family: var(--f-ui); font-size: 11px; font-weight: 600; letter-spacing: 0;
  text-transform: capitalize; padding: 3px 9px; border-radius: var(--r-pill);
  background: var(--accent-tint); color: var(--accent); }
.iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 32px;
  height: 32px; border: 1px solid transparent; border-radius: var(--r); background: none;
  color: var(--ink-3); cursor: pointer; padding: 0; }
.iconbtn:hover { background: var(--surface-2); border-color: var(--line); color: var(--ink); }

/* ---- avatar. Deterministic tint from the name, so one person keeps one
   colour across every screen. It carries no meaning, only recognition. */
.avatar { flex: none; width: 30px; height: 30px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-size: 11.5px; font-weight: 600;
  background: var(--accent-tint-2); color: var(--accent-press); }
.avatar.sm { width: 25px; height: 25px; font-size: 10px; }
.avatar.av-1 { background: var(--teal-tint); color: var(--teal); }
.avatar.av-2 { background: var(--amber-tint); color: var(--amber); }
.avatar.av-3 { background: var(--info-tint); color: var(--info); }
.avatar.av-4 { background: var(--ok-tint); color: var(--ok); }

/* ---- content column. Wide enough to use a big monitor, capped so a line of
   text never runs past a readable length. */
main.appmain { flex: 1; width: 100%; max-width: var(--content-max); margin: 0 auto;
  padding: 26px 26px 80px; }

/* ---- mobile tab bar */
.tabbar { display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background: var(--surface); border-top: 1px solid var(--line);
  box-shadow: 0 -6px 18px -14px rgb(20 16 32 / .35);
  overflow-x: auto; scrollbar-width: none;
  padding: 4px 6px calc(4px + env(safe-area-inset-bottom, 0px)); }
.tabbar::-webkit-scrollbar { display: none; }
.tb-item { flex: none; display: flex; flex-direction: column; align-items: center; gap: 3px;
  min-width: 62px; background: none; border: 0; border-radius: var(--r);
  padding: 6px 8px 5px; font: inherit; font-size: 10.5px; font-weight: 500;
  color: var(--ink-3); cursor: pointer; }
.tb-item.on { color: var(--accent); }
.tb-ico { position: relative; display: inline-flex; }
.tb-ico .count { position: absolute; top: -5px; right: -10px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: var(--r-pill); background: var(--accent); color: #fff;
  font-size: 10px; font-weight: 600; line-height: 16px; text-align: center; }
.tb-lbl { white-space: nowrap; }

@media (max-width: 900px) {
  .app-shell { grid-template-columns: minmax(0, 1fr); }
  .sidebar { display: none; }
  .tabbar { display: flex; }
  .appbar { padding: 0 14px; gap: 10px; }
  .ab-mark { display: block; flex: none; }
  .ab-mark img { width: 26px; height: 26px; border-radius: 7px; display: block; }
  .appsearch { min-width: 0; flex: 1 1 auto; }
  .ab-name { display: none; }
  main.appmain { padding: 18px 14px 110px; }
}
/* The one-thing-to-do-next bar on a job now has to clear the tab bar, and the
   page has to be able to scroll past both of them. :has() keeps that extra
   padding on the one page that needs it instead of every page. */
@media (max-width: 700px) {
  main.appmain { padding-bottom: 110px; }
  main.appmain:has(.nextaction) { padding-bottom: 196px; }
  .nextaction { bottom: calc(56px + env(safe-area-inset-bottom, 0px)); }
}


/* ============================================================================
   SHARED COMPONENTS  (step 3 of the visual pass)

   One button system, one input system, one badge system, one card. Before
   this there were three visual languages on a single row: outlined pills, a
   purple button and two bare native selects. Every rule here overrides
   assets/site.css or assets/app.css, both of which are shared with pages
   outside /app/ and are deliberately left untouched.

   Monospace is now reserved for things that really are code: record ids,
   tokens, keyboard keys. An email address is not code and neither is the word
   "Customer", and setting them in a terminal face was most of why the console
   read as a log file.
   ========================================================================= */

/* ---- page heading */
.vhead { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; flex-wrap: wrap; }
.vhead h2 { font-family: var(--f-display); font-size: var(--t-title); font-weight: 600;
  letter-spacing: -.02em; margin: 0; color: var(--ink); }
.vhead .sub { color: var(--ink-3); font-size: 13.5px; }
.vhead .right { margin-left: auto; display: flex; gap: 8px; align-items: center; }

/* ---- buttons. 36px tall, 8px radius, 500 weight, four kinds and no more.
   The marketing site's .btn is a 52px landing-page call to action; in a tool
   that is a shout. */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 36px; min-height: 0; padding: 0 16px; border-radius: var(--r);
  border: 1px solid var(--accent); background: var(--accent); color: #fff;
  font-family: inherit; font-size: 13.5px; font-weight: 500; line-height: 1;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: background-color .12s, border-color .12s, color .12s; }
.btn:hover { background: var(--accent-hover); border-color: var(--accent-hover);
  color: #fff; transform: none; box-shadow: none; }
.btn:active { background: var(--accent-press); transform: none; }
.btn[disabled] { opacity: .55; cursor: default; }
.btn.ghost { background: var(--surface); border-color: var(--line-2); color: var(--ink); }
.btn.ghost:hover { background: var(--surface-2); border-color: var(--ink-3); color: var(--ink); }
.btn.quiet { background: none; border-color: transparent; color: var(--ink-2); }
.btn.quiet:hover { background: var(--surface-3); border-color: transparent; color: var(--ink); }
.btn.danger { background: var(--stop); border-color: var(--stop); }
.btn.danger:hover { background: #8F1C13; border-color: #8F1C13; }
.btn.solid-brand { background: var(--teal); border-color: var(--teal); }
.btn.solid-brand:hover { background: var(--teal-hover); border-color: var(--teal-hover); }
.btn.sm { height: 32px; padding: 0 13px; font-size: 13px; }
.btn.tiny { height: 27px; padding: 0 10px; font-size: 12px; }
.btn.wide { width: 100%; }
.btn .ico { width: 16px; height: 16px; }
.linkbtn { background: none; border: 0; color: var(--accent); font: inherit; font-size: 13.5px;
  cursor: pointer; padding: 3px 5px; border-radius: var(--r-xs); }
.linkbtn:hover { color: var(--accent-hover); background: var(--accent-tint); }

/* ---- inputs, and a select that does not look like 1998. The chevron is an
   inline data URI, so it is still "no external load". */
.f { display: grid; gap: 6px; }
.f label { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.f input, .f textarea, .toolbar-search input, .combo-input {
  font: inherit; font-size: 14px; color: var(--ink); background: var(--paper);
  border: 1px solid var(--line-2); border-radius: var(--r); height: var(--control-h);
  padding: 0 11px; width: 100%; }
.f textarea { height: auto; min-height: 94px; padding: 10px 11px; resize: vertical; line-height: 1.55; }
.f input:focus, .f select:focus, .f textarea:focus,
.toolbar-search input:focus, .combo-input:focus, #gs-q:focus {
  outline: 0; border-color: var(--accent); box-shadow: var(--ring); }
.f input::placeholder, .f textarea::placeholder, .toolbar-search input::placeholder,
.combo-input::placeholder { color: var(--ink-3); opacity: 1; }
body.app select {
  -webkit-appearance: none; appearance: none;
  font: inherit; font-size: 13.5px; color: var(--ink); background-color: var(--paper);
  border: 1px solid var(--line-2); border-radius: var(--r); height: var(--control-h);
  padding: 0 32px 0 11px; width: 100%; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236E6780' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9.5 12 15.5 18 9.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center; background-size: 15px; }
body.app select:hover { border-color: var(--ink-3); }
.tb-sort { width: auto; min-width: 160px; flex: none; }

/* ---- toolbar */
.toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 14px; flex-wrap: wrap; }
.toolbar-search { position: relative; flex: 1 1 300px; max-width: 440px; min-width: 220px; }
.toolbar-search input { padding-right: 34px; }
.tb-clear { position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; color: var(--ink-3); font-size: 17px; line-height: 1;
  cursor: pointer; padding: 3px 6px; border-radius: var(--r-xs); }
.tb-clear:hover { color: var(--ink); background: var(--surface-2); }

/* ---- segmented sub-tabs. One rounded track, the active segment tinted.
   Replaces a row of loose outlined pills with a dark teal fill on the active
   one, which was the loudest thing on most screens. */
.subtabs { display: inline-flex; gap: 2px; margin-bottom: 18px; padding: 3px; max-width: 100%;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md);
  overflow-x: auto; scrollbar-width: none; vertical-align: top; }
.subtabs::-webkit-scrollbar { display: none; }
.subtab { background: none; border: 0; border-radius: var(--r-sm); padding: 6px 12px;
  font: inherit; font-size: 13px; font-weight: 500; color: var(--ink-2); cursor: pointer;
  white-space: nowrap; flex: none; display: flex; align-items: center; gap: 7px; }
.subtab:hover { background: var(--surface); color: var(--ink); }
.subtab.on { background: var(--accent-tint); color: var(--accent); font-weight: 600; }
.subtab .n { font-family: var(--f-ui); font-size: 11.5px; font-weight: 600; color: var(--ink-3);
  background: var(--surface-3); border-radius: var(--r-pill); padding: 1px 6px;
  font-variant-numeric: tabular-nums; }
.subtab.on .n { background: rgb(108 60 144 / .14); color: var(--accent); opacity: 1; }

/* ---- badges. Tinted ground, matching text, no border, real sentence case.
   One palette across jobs, quotes, invoices, contacts and documents, so a
   colour means the same thing wherever it turns up. Shape still carries the
   two that must never be misread: lost and declined keep their line-through. */
.badge, .st { display: inline-flex; align-items: center; gap: 5px; font-family: var(--f-ui);
  font-size: 12px; font-weight: 600; letter-spacing: 0; text-transform: none;
  padding: 3px 9px; border-radius: var(--r-pill); border: 0; white-space: nowrap;
  background: var(--surface-2); color: var(--ink-2); }
.badge-neutral { background: var(--surface-2); color: var(--ink-2); }
.badge-accent { background: var(--accent-tint); color: var(--accent); }
.badge-ok, .st-success { background: var(--ok-tint); color: var(--ok); }
.badge-info { background: var(--info-tint); color: var(--info); }
.badge-amber, .st-warn { background: var(--amber-tint); color: var(--amber); }
.badge-money, .st-money { background: var(--money-tint); color: var(--money); }
.badge-stop, .st-danger { background: var(--stop-tint); color: var(--stop); }
.badge-teal { background: var(--teal-tint); color: var(--teal); }
.st-new { background: var(--accent); color: #fff; }
.st-open { background: var(--accent-tint); color: var(--accent); }
.st-done { background: var(--surface-2); color: var(--ink-3); }
.st-lost { background: var(--surface-2); color: var(--ink-3); text-decoration: line-through; }
.st-danger { text-decoration: line-through; }

/* ---- stage pill. Six segments filled up to where the job is, plus the stage
   in words. It replaces the six-stage stepper that used to sit above every
   row of the Jobs list. */
.stagepill { display: inline-flex; align-items: center; gap: 10px; }
.sp-track { display: inline-flex; gap: 3px; align-items: center; }
.sp-seg { display: block; width: 13px; height: 4px; border-radius: 2px; background: var(--line); }
.sp-seg.done { background: var(--accent); opacity: .42; }
.sp-seg.now { background: var(--accent); width: 18px; }

/* ---- cards and rows */
.panel { border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-lg);
  padding: 20px; margin-bottom: 18px; box-shadow: var(--sh-sm); }
.panel h3 { font-family: var(--f-display); font-size: var(--t-card); font-weight: 600;
  margin: 0 0 16px; letter-spacing: -.008em; }
.grid2 { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.rows { display: flex; flex-direction: column; gap: 8px; }
.row { border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-lg);
  padding: 14px 16px; display: flex; gap: 14px; align-items: center; box-shadow: var(--sh-sm);
  transition: border-color .12s, box-shadow .12s; }
.row:hover { border-color: var(--line-2); box-shadow: var(--sh-md); }
/* The coloured left bar is gone: status is a badge now, and a hairline box
   with a stripe was most of why nothing read as raised. */
.row .stripe { display: none; }
.row .t { font-weight: 600; font-size: 14.5px; color: var(--ink); }
.row .m { color: var(--ink-2); font-size: 13.5px; margin-top: 2px; overflow-wrap: anywhere; }
.row .meta { font-family: var(--f-ui); font-size: 13px; color: var(--ink-3); margin-top: 4px;
  display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
.row .meta a { color: var(--ink-2); text-decoration: none; }
.row .meta a:hover { color: var(--accent); text-decoration: underline; }
.row .acts { display: flex; gap: 8px; align-items: center; flex: none; flex-wrap: wrap;
  justify-content: flex-end; max-width: 380px; }
.tile { border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-lg);
  padding: 16px 18px; box-shadow: var(--sh-sm); }
.tile .k { font-family: var(--f-ui); font-size: 12.5px; font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--ink-3); }
.tile .v { font-family: var(--f-display); font-size: 30px; font-weight: 600; letter-spacing: -.03em;
  margin-top: 4px; color: var(--ink); }

/* ---- empty states. A solid card with an icon, a heading and one sentence,
   instead of a dashed box with a paragraph of grey text in it. */
.empty { color: var(--ink-3); font-size: 14px; padding: 30px 24px; text-align: center;
  border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-lg);
  box-shadow: var(--sh-sm); }
.emptystate { display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 7px; padding: 42px 24px; border: 1px solid var(--line); background: var(--surface);
  border-radius: var(--r-lg); box-shadow: var(--sh-sm); }
.es-ico { display: inline-flex; align-items: center; justify-content: center; width: 46px;
  height: 46px; border-radius: var(--r-lg); background: var(--accent-tint); color: var(--accent);
  margin-bottom: 4px; }
.es-ico .ico { width: 22px; height: 22px; }
.emptystate h3 { margin: 0; font-size: 15.5px; font-weight: 600; color: var(--ink); }
.emptystate p { margin: 0; font-size: 13.5px; color: var(--ink-3); max-width: 56ch; line-height: 1.55; }
.es-act { margin-top: 12px; display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }

/* ---- banners */
.err { background: var(--stop-tint); color: var(--stop); border: 1px solid rgb(180 35 24 / .22);
  border-radius: var(--r); padding: 12px 14px; font-size: 13.5px; margin-bottom: 16px; }
.ok { background: var(--ok-tint); color: var(--ok); border: 1px solid rgb(31 107 58 / .22);
  border-radius: var(--r); padding: 12px 14px; font-size: 13.5px; margin-bottom: 16px; }

/* ---- the monospace cull. Everything listed here was set in IBM Plex Mono
   and is not code. What stays in mono: the Ctrl K hint, inline <code> in
   Settings, and anything given the .mono class (record ids, tokens). */
.row .meta, .subtab .n, .bubble .who, .rail-group, .gs-group, .tile .k, .combo-opt .hint,
.wizstep .n, .gs-foot b, .stepper .step .lbl, .stepper-lost, .st, .badge, .role,
.wiztotal b, .appnav .count { font-family: var(--f-ui); }
.mono { font-family: var(--f-mono); font-size: .94em; letter-spacing: -.01em; }

/* ---- panels and lists keep their entrance, buttons lose the squash */
.btn:active { transform: none; }
@media (prefers-reduced-motion: reduce) {
  .row, .panel, .tile { animation: none; }
}


/* ============================================================================
   JOBS  (step 4 of the visual pass)
   ========================================================================= */

/* ---- the job detail stepper. Six real numbers, sans labels, a tick on what
   is done, and no pulse: one dot breathing on a detail page was fine, but it
   was firing on every row of a twenty-row list, which is a nervous tic. */
.jsteps { display: flex; list-style: none; margin: 0; padding: 2px 0 0; gap: 0; }
.jstep { flex: 1 1 0; min-width: 0; position: relative; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 8px; }
.jstep::after { content: ""; position: absolute; top: 13px; left: calc(50% + 19px);
  right: calc(-50% + 19px); height: 2px; background: var(--line); border-radius: 1px; }
.jstep:last-child::after { display: none; }
.jstep.done::after { background: var(--accent); opacity: .42; }
.jstep-mark { position: relative; z-index: 1; flex: none; width: 27px; height: 27px;
  border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
  background: var(--surface); color: var(--ink-3); border: 1px solid var(--line-2); }
.jstep-mark .ico { width: 14px; height: 14px; }
.jstep.done .jstep-mark { background: var(--accent-tint); color: var(--accent); border-color: transparent; }
.jstep.now .jstep-mark { background: var(--accent); color: #fff; border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-tint); }
.jstep.money .jstep-mark { background: var(--money); color: #fff; border-color: var(--money);
  box-shadow: 0 0 0 4px var(--money-tint); }
.jstep-lbl { font-size: 12.5px; line-height: 1.25; color: var(--ink-3); }
.jstep.done .jstep-lbl { color: var(--ink-2); }
.jstep.now .jstep-lbl, .jstep.money .jstep-lbl { color: var(--ink); font-weight: 600; }
.stepper-lost { font-family: var(--f-ui); font-size: 13.5px; font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--stop); background: var(--stop-tint);
  border-radius: var(--r); padding: 12px 14px; text-align: left; }
.jobhead { padding-bottom: 20px; }
.nextaction { display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line); }
.nextaction .sub { flex: 1 1 260px; min-width: 0; color: var(--ink-3); font-size: 13px; line-height: 1.5; }

/* On a phone the six steps stack, because six labels across 390px is unreadable. */
@media (max-width: 640px) {
  .jsteps { flex-direction: column; gap: 0; align-items: stretch; }
  .jstep { flex: none; flex-direction: row; align-items: center; gap: 12px; text-align: left;
    padding: 6px 0; }
  .jstep::after { top: 34px; bottom: -6px; left: 13px; right: auto; width: 2px; height: auto; }
  .jstep-lbl { font-size: 13.5px; }
}

/* ---- job rows. The stepper is gone from here; the stage is a pill on the
   right and the title is the first thing the eye lands on. */
.jobrow { min-height: 64px; }
.jobrow .body { flex: 1; min-width: 0; }
.jobrow .t a { color: inherit; text-decoration: none; }
.jobrow .t a:hover { color: var(--accent); text-decoration: underline; }
.row .t a { color: inherit; text-decoration: none; }
.row .t a:hover { color: var(--accent); text-decoration: underline; }

/* ---- menu. What the bare <select> in the middle of a row of buttons became. */
.menu { position: relative; display: inline-flex; }
.menu-btn .ico { width: 14px; height: 14px; margin-left: -2px; }
.menu-pop { position: absolute; z-index: 60; top: calc(100% + 5px); right: 0; min-width: 190px;
  display: flex; flex-direction: column; padding: 5px; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--sh-lg); }
.menu-item { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: none; border: 0; border-radius: var(--r-sm); padding: 8px 10px; font: inherit;
  font-size: 13.5px; color: var(--ink); cursor: pointer; white-space: nowrap; }
.menu-item:hover { background: var(--surface-2); }
.menu-item.on { color: var(--accent); font-weight: 600; }
.menu-tick { margin-left: auto; display: inline-flex; }
.menu-tick .ico { width: 15px; height: 15px; }


/* ============================================================================
   LIST VIEWS  (step 5 of the visual pass)
   ========================================================================= */

/* Status words come out of the database lowercase ("sent", "cancelled").
   Capitalising only the first letter keeps "In progress" as a sentence rather
   than turning it into Title Case. */
.st::first-letter, .badge::first-letter { text-transform: uppercase; }

.contactrow { cursor: pointer; }
.row[data-openticket], .row[data-openquote], .row[data-openconvo] { cursor: pointer; }
.t-sub { color: var(--ink-3); font-weight: 400; }
.row .t .t-sub::before { content: "\00b7"; margin: 0 5px; color: var(--line-2); }

/* The conversation thread on Inbox and the contact page. */
.thread { display: flex; flex-direction: column; gap: 12px; margin: 16px 0 20px; }
.bubble { border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-lg);
  padding: 13px 15px; max-width: 78%; box-shadow: var(--sh-sm); }
.bubble.me { align-self: flex-end; background: var(--accent-tint); border-color: transparent; }
.bubble .who { font-family: var(--f-ui); font-size: 11.5px; font-weight: 600; letter-spacing: 0;
  text-transform: none; color: var(--ink-3); margin-bottom: 5px; }
.bubble.me .who { color: var(--accent); }
.authtoggle { display: flex; gap: 8px; margin-bottom: 16px; }

/* The combobox picker, matching the input system. */
.combo-input { height: var(--control-h); }
.combo-list { border-radius: var(--r-md); border-color: var(--line); box-shadow: var(--sh-lg);
  margin-top: 4px; padding: 5px; }
.combo-opt { border-bottom: 0; border-radius: var(--r-sm); padding: 8px 10px; font-size: 13.5px; }
.combo-opt:hover, .combo-opt.on { background: var(--surface-2); }
.combo-opt .hint { font-family: var(--f-ui); font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.combo-none { padding: 14px 11px; font-size: 13.5px; color: var(--ink-3); }

/* Highlighted search matches. */
mark { background: rgb(108 60 144 / .16); color: inherit; border-radius: 3px; padding: 0 2px; }

/* The global search palette. */
.gs-back { background: rgb(23 20 31 / .42); padding: 9vh 16px 16px; }
.gs-panel { border-radius: var(--r-xl); border-color: var(--line); box-shadow: var(--sh-lg); }
#gs-q { font-size: 16px; padding: 17px 19px; border-radius: 0; }
.gs-group { font-family: var(--f-ui); font-size: 11.5px; font-weight: 600; letter-spacing: .01em;
  text-transform: none; color: var(--ink-3); padding: 13px 19px 5px; }
.gs-hit { padding: 10px 19px; }
.gs-hit:hover, .gs-hit.on { background: var(--surface-2); }
.gs-hit-title { font-size: 14px; font-weight: 600; }
.gs-hit-sub { font-size: 12.5px; color: var(--ink-3); }
.gs-foot { background: var(--surface-2); font-size: 12px; padding: 10px 19px; }
.gs-foot b { font-family: var(--f-mono); font-weight: 600; }


/* ============================================================================
   DASHBOARD, SETTINGS, QUOTE WIZARD  (steps 6 to 8 of the visual pass)
   ========================================================================= */

/* ---- dashboard. Four tiles across, then what is waiting on you beside what
   is coming up, so a wide screen shows both at once. */
.tiles { display: grid; gap: 12px; grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-bottom: 26px; }
a.tile { display: block; text-decoration: none; color: inherit; transition: border-color .12s, box-shadow .12s; }
a.tile:hover { border-color: var(--line-2); box-shadow: var(--sh-md); }
a.tile .k, a.tile .v { color: inherit; }
.tile.alert .v, a.tile.alert .v { color: var(--accent); }
.dashcols { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 24px;
  align-items: start; }
.dashcol { min-width: 0; }
.dashcol .vhead { margin-bottom: 12px; }
.h-sec { font-size: 16px; }
.dash-sub { font-family: var(--f-ui); font-size: 12.5px; font-weight: 600; color: var(--ink-3);
  margin: 20px 0 9px; }
.dashcol > .dash-sub:first-of-type { margin-top: 0; }
@media (max-width: 1040px) {
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dashcols { grid-template-columns: minmax(0, 1fr); gap: 10px; }
}

/* ---- settings. The rail is a proper card in the content area rather than a
   bare list, and each section still saves on its own. */
.settings-layout { display: grid; grid-template-columns: 236px minmax(0, 1fr); gap: 24px;
  align-items: start; }
.rail { position: sticky; top: calc(var(--topbar-h) + 18px); display: flex; flex-direction: column;
  gap: 1px; border: 1px solid var(--line); background: var(--surface); border-radius: var(--r-lg);
  padding: 8px; box-shadow: var(--sh-sm); }
.rail-group { font-family: var(--f-ui); font-size: 11px; font-weight: 600; letter-spacing: .02em;
  text-transform: none; color: var(--ink-3); padding: 13px 10px 5px; }
.rail-group:first-child { padding-top: 5px; }
.rail-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  border-radius: var(--r-sm); font-size: 13.5px; color: var(--ink-2); text-decoration: none; }
.rail-item:hover { background: var(--surface-2); color: var(--ink); }
.rail-item.on { background: var(--accent-tint); color: var(--accent); font-weight: 600; }
.rail-dot { width: 8px; height: 8px; border-radius: 50%; flex: none;
  border: 2px solid var(--line-2); box-sizing: border-box; }
.rail-dot.on { background: var(--ok); border-color: var(--ok); }
.savebar { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line);
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.settings-body code { font-family: var(--f-mono); font-size: .92em; background: var(--surface-2);
  padding: 2px 5px; border-radius: var(--r-xs); }
.secret-state { margin-top: 6px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
@media (max-width: 900px) {
  .settings-layout { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .rail { position: static; flex-direction: row; overflow-x: auto; scrollbar-width: none;
    gap: 4px; padding: 5px; }
  .rail-group { display: none; }
  .rail-item { white-space: nowrap; flex: none; }
}

/* ---- quote wizard. Same language as the job stepper: a numbered mark, the
   current step ringed in the accent, finished steps tinted. */
.wizsteps { display: flex; gap: 6px; margin-bottom: 18px; overflow-x: auto;
  scrollbar-width: none; padding: 2px 2px 4px; }
.wizstep { display: flex; align-items: center; gap: 9px; flex: none; white-space: nowrap;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 8px 14px; font: inherit; font-size: 13px; color: var(--ink-3); cursor: pointer;
  box-shadow: var(--sh-sm); }
.wizstep:hover { border-color: var(--line-2); color: var(--ink); }
.wizstep .n { width: 21px; height: 21px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-size: 11.5px; font-weight: 600;
  background: var(--surface-2); color: var(--ink-3); flex: none; font-variant-numeric: tabular-nums; }
.wizstep.on { border-color: var(--accent); color: var(--ink); font-weight: 600;
  box-shadow: 0 0 0 3px var(--accent-tint); }
.wizstep.on .n { background: var(--accent); color: #fff; }
.wizstep.done { color: var(--ink-2); }
.wizstep.done .n { background: var(--accent-tint); color: var(--accent); }
.wizrow { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap;
  margin-bottom: 14px; padding-bottom: 14px; border-bottom: 1px dashed var(--line); }
.wiztotal { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); font-size: 14px; }
.wiztotal b { font-family: var(--f-display); font-size: 21px; font-weight: 600; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; margin: 0 5px; }
.wiznav { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 6px; flex-wrap: wrap; }
@media (max-width: 700px) {
  .wiznav { position: sticky; bottom: 0; background: var(--surface); padding: 12px 0;
    border-top: 1px solid var(--line); }
}

/* ---- the sign-in screen, which is the first thing anyone sees. */
.login { max-width: 400px; margin: 11vh auto; }
.login .panel { padding: 30px; border-radius: var(--r-xl); box-shadow: var(--sh-md); }
.login h1 { font-family: var(--f-display); font-size: 22px; font-weight: 600; margin: 0 0 4px;
  letter-spacing: -.02em; }
.login p.sub { color: var(--ink-3); font-size: 13.5px; margin: 0 0 20px; }


/* ============================================================================
   COLLISION GUARD

   The console loads assets/site.css first, for the fonts and the base reset,
   and the marketing site defines some of the same generic class names for its
   own components. Anything this file does not explicitly set on those names
   leaks straight through at equal specificity.

   That is not a theory. Two of them were caught on screen during this pass:
   the job stepper used class="step", and site.css gives .step a ::before of
   "0" counter(s) with no counter-reset in scope, which is why every stage
   read "01"; and the dashboard tiles use class="tile", which site.css gives
   min-height: 260px and a hover lift, making every tile four times taller
   than its contents. The stepper was renamed .jstep. The tiles and buttons
   are used all over the console, so their leaks are reset here instead.

   The full leak list, measured by matching every site.css rule against the
   live DOM of all twelve console screens: .btn (and .ghost, .sm, :hover,
   :active), .tile, .tiles, .tile:hover, section, a:hover. If a new generic
   class name is ever added to the console, re-run that check before
   assuming it is free.
   ========================================================================= */
.tiles { gap: 12px; }
.tile { position: static; display: block; gap: 0; min-height: 0;
  transition: border-color .12s, box-shadow .12s; }
.tile:hover, a.tile:hover { transform: none; }
.btn { min-height: 0; line-height: 1; }
body.app section { padding: 0; margin: 0; }


/* ---- narrow rows. On a phone the actions column wants the full width, but
   .row does not wrap, so .acts and .body fought each other for 390px and a
   job row came out 227px tall. Wrapping the row puts the avatar and the text
   on the first line and the badges on the second. Below 470px the six stage
   segments are dropped too: the stage is still there in words, which is the
   part that carries the meaning. */
@media (max-width: 620px) {
  .row { flex-wrap: wrap; align-items: center; row-gap: 10px; }
  .row .body { flex: 1 1 200px; }
  .row .acts { width: 100%; max-width: none; justify-content: flex-start; }
}
@media (max-width: 470px) {
  .sp-track { display: none; }
  .stagepill { gap: 0; }
}
@media (max-width: 620px) {
  .row { padding: 12px 14px; }
  .row .meta { gap: 4px 12px; font-size: 12.5px; }
}


/* ---- two things the first screenshot of the Jobs list caught.

   1. The sort control was rendering full width and pushing itself onto its
      own line. "body.app select" is specificity (0,1,1) and beat ".tb-sort"
      at (0,1,0), so width:100% won. Matching the specificity fixes it.
   2. The actions column wrapped, so a row carrying an extra badge (a portal
      invite prompt, or a payment state) came out half again as tall as its
      neighbours and the list looked ragged. On a desktop the actions stay on
      one line and the title text shrinks instead; the phone rule below 620px
      still wraps them onto their own row. */
body.app select.tb-sort { width: auto; min-width: 170px; }
.row .acts { flex-wrap: nowrap; max-width: none; flex: 0 0 auto; }
@media (max-width: 620px) {
  .row .acts { flex-wrap: wrap; width: 100%; flex: 1 1 100%; }
}

/* On a phone the old rule hid the search label and the Ctrl K chip together,
   which left a wide empty grey box with a magnifier in it. The keyboard hint
   is the part that means nothing on a phone; the words are what make the box
   look like something you can tap. */
@media (max-width: 700px) {
  .appsearch-t { display: block; }
  .appsearch-k { display: none; }
}

/* A select inside a row's actions was collapsing to a sliver once that column
   stopped wrapping: "body.app select" sets width:100%, which a nowrap flex
   line then squeezed to nothing. The enquiry rows on the dashboard showed it
   first, as a 40px stub reading "new". */
.row .acts select { width: auto; min-width: 132px; flex: none; }

/* The logo upload is the one native control left on screen. ::file-selector-button
   is pure CSS, so this changes how the button looks and nothing about how the
   upload works. */
body.app input[type="file"] { font: inherit; font-size: 13.5px; color: var(--ink-2); }
body.app input[type="file"]::file-selector-button {
  font: inherit; font-size: 13px; font-weight: 500; height: 32px; padding: 0 13px;
  margin-right: 10px; border-radius: var(--r); border: 1px solid var(--line-2);
  background: var(--surface); color: var(--ink); cursor: pointer; }
body.app input[type="file"]::file-selector-button:hover {
  background: var(--surface-2); border-color: var(--ink-3); }

/* The sign-in card is the first thing anyone sees, and it was the one screen
   with no brand on it at all. */
.login-brand { display: flex; align-items: center; gap: 13px; margin-bottom: 24px; }
.login-brand img { width: 40px; height: 40px; border-radius: 11px; display: block; flex: none; }
.login h1 { margin: 0; }
.login p.sub { margin: 2px 0 0; }
