:root {
    --forest: #1f2124; --forest-2: #2c2f33; --sage: #4a4f56; --mint: #b8b3aa; --cream: #f6f4f0;
    --warm-white: #fffdfa; --gold: #a33236; --charcoal: #1f2124;
    --mid-grey: #6b6f75; --light-grey: #e4e0d9; --red: #a84438;
    --shadow: 0 4px 24px rgba(31,33,36,0.08); --shadow-lg: 0 8px 40px rgba(31,33,36,0.16);
    --gold-deep: #8a2a2e;
  /* Taking money is a quiet, everyday act: a softer olive-gold that sits on
     the forest panel without competing with the primary gold. */
  --pay-gold: #a33236;
    --sidebar-w: 232px;
    --font-display: 'Outfit', sans-serif;
    --font-body: 'DM Sans', sans-serif;
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  /* The page itself never pans sideways; only inner carousels scroll, so a
     swipe on the background cannot drag the site off its edges. */
  /* overflow-x: clip (not hidden) — hidden turns body into a scroll
     container, which silently kills position:sticky for everything inside
     (the job form's left rail stopped pinning). clip blocks sideways
     panning the same way without creating a scroller; hidden stays first
     as the fallback for browsers without clip. */
  html { overflow-x: hidden; overflow-x: clip; overscroll-behavior-x: none; -webkit-tap-highlight-color: transparent; }
  /* The home-screen app ignores the format-detection meta (WKWebView
     standalone), so iOS wraps phone numbers in its own blue underlined
     links as the list renders. Restyle whatever it injects. */
  a[x-apple-data-detectors] { color: inherit !important; text-decoration: none !important; }
  /* Each engine tags its injected data-detector links differently (the rule
     above catches WebKit's), so the address lines get a blanket version:
     they are rendered as escaped text, never real links, so any anchor found
     inside one was injected by a browser and wears the text's own look. */
  .job-card-address a, .contact-meta a { color: inherit !important; text-decoration: none !important; font-weight: inherit !important; }
  /* manipulation kills double-tap-to-zoom, which is the zoom that fires by
     accident when someone taps twice quickly on a small control. Panning and
     pinch still work, so anyone who needs to enlarge the page still can; the
     signature pad sets touch-action: none for itself and is unaffected. */
  body { font-family: var(--font-body); background: var(--cream); color: var(--charcoal); min-height: 100vh; max-width: 100%; overflow-x: hidden; overflow-x: clip; overscroll-behavior-x: none; touch-action: manipulation; }
  .serif { font-family: var(--font-display); }
  .stat-value, .job-card-total, .totals-row.main, .blind-price-tag, .doc-total-row.grand { font-feature-settings: "lnum" 1; font-variant-numeric: lining-nums; }

  .ic { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex: none; }
  .ic.sm { width: 15px; height: 15px; }
  .ic.lg { width: 44px; height: 44px; stroke-width: 1.5; }

  /* ── SIDEBAR (desktop) ─────────────────────────────── */
  .sidebar { position: fixed; top: 0; left: 0; bottom: 0; width: var(--sidebar-w); background: var(--forest); display: flex; flex-direction: column; padding: 26px 14px 18px; z-index: 100; }
  /* Centred lockup with a hairline under it, so the brand reads as its own
     block rather than the first item in the nav list. */
  .brand { display: block; padding: 2px 10px 18px; margin-bottom: 18px; cursor: pointer; text-align: center; border-bottom: 1px solid rgba(184,179,170,0.16); }
  /* These three are links, and a link's underline is painted in the link's
     own colour: without this the browser's blue underline sat under the
     white brand text (owner, 20 Sep). */
  .brand, .side-new, .mob-new { color: inherit; text-decoration: none; }
  .brand h1 { font-family: var(--font-display); color: var(--warm-white); font-size: 16.5px; font-weight: 700; line-height: 1.25; letter-spacing: 0.2px; white-space: nowrap; }
  .brand p { color: var(--mint); font-size: 10px; margin-top: 6px; letter-spacing: 2.2px; text-transform: uppercase; font-weight: 500; }
  .side-new { display: flex; align-items: center; justify-content: center; gap: 8px; margin: 0 6px 20px; padding: 12px; background: var(--gold); color: var(--forest); border: none; border-radius: 12px; font-family: var(--font-body); font-size: 13.5px; font-weight: 700; cursor: pointer; transition: all 0.2s; }
  .side-new:hover { background: #8a2a2e; transform: translateY(-1px); }
  .side-nav { display: flex; flex-direction: column; gap: 3px; }
  .side-link { display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: 10px; color: rgba(246,244,240,0.72); font-size: 13.5px; font-weight: 500; cursor: pointer; text-decoration: none; transition: all 0.15s; }
  .side-link:hover { background: rgba(255,255,255,0.06); color: var(--warm-white); }
  .side-link.active { background: rgba(184,179,170,0.15); color: #fff; font-weight: 650; }
  .side-link .soon { margin-left: auto; font-size: 9px; letter-spacing: 0.8px; text-transform: uppercase; color: var(--mint); opacity: 0.65; font-weight: 600; }
  /* Settings drops to the bottom of the nav area, just above the hairline;
     Log out and the version line sit below it, on the same left edge. */
  .side-foot { display: flex; flex-direction: column; gap: 3px; margin-top: auto; padding: 14px 0 0; border-top: 1px solid rgba(184,179,170,0.16); }
  /* Sized to match .side-link so Settings and Log out read as a pair. */
  .lock-btn { display: flex; align-items: center; gap: 12px; width: 100%; padding: 11px 12px; background: none; border: none; color: rgba(246,244,240,0.72); font-family: var(--font-body); font-size: 13.5px; font-weight: 500; cursor: pointer; border-radius: 10px; transition: all 0.15s; text-align: left; }
  .lock-btn:hover { color: var(--warm-white); background: rgba(255,255,255,0.06); }
  .side-ver { display: flex; justify-content: space-between; gap: 10px; padding: 8px 12px 0; font-size: 10.5px; color: rgba(184,179,170,0.45); }

  /* ── MOBILE BARS ───────────────────────────────────── */
  .mobilebar { display: none; position: sticky; top: 0; z-index: 100; background: var(--forest); padding: calc(14px + env(safe-area-inset-top)) 16px 14px; align-items: center; justify-content: space-between; box-shadow: var(--shadow-lg); }
  .mobilebar h1 { font-family: var(--font-display); color: var(--warm-white); font-size: 16px; font-weight: 700; }
  .mobilebar p { color: var(--mint); font-size: 9px; letter-spacing: 1.8px; text-transform: uppercase; margin-top: 1px; }
  .mob-new { display: flex; align-items: center; gap: 6px; padding: 9px 16px; background: var(--gold); color: var(--forest); border: none; border-radius: 20px; font-family: var(--font-body); font-size: 12.5px; font-weight: 700; cursor: pointer; }
  .tabbar { display: none; position: fixed; left: 0; right: 0; bottom: 0; z-index: 100; background: var(--forest); border-top: 1px solid rgba(184,179,170,0.18); padding: 8px 4px calc(8px + env(safe-area-inset-bottom)); justify-content: space-around; }
  /* Five tabs share the bar, so padding gives way before the labels do. */
  .tab-link { display: flex; flex-direction: column; align-items: center; gap: 3px; color: rgba(246,244,240,0.6); font-size: 10px; font-weight: 600; text-decoration: none; padding: 4px 8px; border-radius: 8px; white-space: nowrap; }
  .tab-link.active { color: var(--gold); }
  .tab-link .ic { width: 20px; height: 20px; }
  /* Glass dock, resting pill. Hidden unless the preference is on AND the
     dock has tucked away; both live on body classes so nothing here can
     touch the flat bar. Deliberately matt: no glow, no underline. */
  .dock-pill { position: fixed; left: 0; right: 0; width: max-content; margin: 0 auto; bottom: calc(14px + env(safe-area-inset-bottom)); z-index: 101; display: none; align-items: center; gap: 10px; padding: 14px 16px; border: 1px solid rgba(246,244,240,0.15); border-radius: 24px; background: rgba(24,26,29,0.9); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); cursor: pointer; opacity: 0; transform: translateY(24px); pointer-events: none; transition: opacity 0.3s, transform 0.35s cubic-bezier(0.4,0,0.2,1); }
  .pill-dot { width: 12px; height: 12px; border-radius: 50%; background: rgba(246,244,240,0.28); }
  .pill-dot.active { background: var(--gold); }
  .pipe-fab { display: none; }

  /* ── LAYOUT ────────────────────────────────────────── */
  .main { margin-left: var(--sidebar-w); min-width: 0; }
  .view { display: none; padding: 30px 28px; max-width: 1200px; margin: 0 auto; }
  #view-pipeline { max-width: 1600px; }
  .view.active { display: block; }
  .page-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; }
  .page-title { font-family: var(--font-display); font-size: 24px; font-weight: 700; color: var(--forest); letter-spacing: -0.01em; }
  /* The figure cards left the pipeline (owner, 14 Aug); the count keeps
     their one essential answer, right where the eye already is. */
  .ic.spin { animation: dh-spin 0.8s linear infinite; }
  @keyframes dh-spin { to { transform: rotate(360deg); } }
  @media (prefers-reduced-motion: reduce) { .ic.spin { animation: none; } }
  .pipe-count { display: inline-block; vertical-align: 3px; font-family: var(--font-body); font-size: 12px; font-weight: 700; color: var(--sage); background: rgba(74,79,86,0.11); padding: 3px 10px; border-radius: 999px; margin-left: 8px; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }

  /* ── PIPELINE ──────────────────────────────────────── */
  .new-job-btn { display: inline-flex; align-items: center; gap: 8px; padding: 12px 22px; background: var(--sage); color: white; border: none; border-radius: 12px; font-family: var(--font-body); font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
  .new-job-btn:hover { background: var(--forest); transform: translateY(-1px); }
  .search-wrap { position: relative; margin-bottom: 20px; }
  .search-wrap .ic { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); color: var(--mid-grey); }
  .search-wrap input { width: 100%; padding: 13px 18px 13px 46px; border: 1.5px solid var(--light-grey); border-radius: 12px; font-size: 14.5px; font-family: var(--font-body); background: var(--warm-white); outline: none; transition: border-color 0.2s, box-shadow 0.2s; }
  .search-wrap input:focus { border-color: var(--sage); box-shadow: 0 0 0 3px rgba(74,79,86,0.12); }

  .pipeline-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px; }
  .stat-card { background: var(--warm-white); border-radius: 14px; padding: 20px; border: 1px solid var(--light-grey); box-shadow: var(--shadow); }
  .stat-label { font-size: 11px; color: var(--mid-grey); text-transform: uppercase; letter-spacing: 0.8px; font-weight: 600; }
  .stat-value { font-family: var(--font-display); font-size: 26px; font-weight: 700; color: var(--forest); margin-top: 6px; line-height: 1.1; }
  .stat-sub { font-size: 12px; color: var(--sage); margin-top: 3px; }

  .pipeline-board { display: grid; grid-template-columns: repeat(10, 1fr); gap: 14px; overflow-x: auto; padding-bottom: 16px; }
  /* Columns sit on a soft panel wash (Basecamp-style) so each stage reads as
     its own lane without shouting. */
  /* The lane is a flex column so the card list can fill whatever height the
     tallest stage sets. Without that the drop target stops at the last card
     and the empty space below it silently rejects the drag. */
  .pipeline-col { min-width: 280px; background: rgba(31,33,36,0.035); border-radius: 14px; padding: 7px 4px 8px; display: flex; flex-direction: column; }
  .col-header { margin: 3px 6px 9px; display: flex; align-items: center; justify-content: center; gap: 6px; padding: 9px 12px; border-radius: 10px; font-size: 10.5px; font-weight: 700; text-align: center; letter-spacing: 0.6px; text-transform: uppercase; }
  /* One glint on arrival from a money row, so the eye lands on the right column. */
  .col-header.col-flash { outline: 3px solid var(--gold); outline-offset: 2px; }
  .col-header .ic { width: 13px; height: 13px; }
  .col-header.measure { background: #f7e8e0; color: #b05e3d; }
  .col-header.quoted { background: #e7edf5; color: #44669a; }
  .col-header.deposit { background: #f5edd6; color: #97781f; }
  .col-header.production { background: #e4efe7; color: #4a4f56; }
  .col-header.fitting { background: #ece5f4; color: #71519b; }
  .col-header.ordered { background: #e0ecf1; color: #2e5e73; }
  .col-header.received { background: #dff0ec; color: #2a7462; }
  .col-header.repair { background: #fae3d7; color: #bf4123; }
  .col-header.complete { background: #e6e4e0; color: var(--forest); }
  .col-header.hold { background: #f6e3e1; color: var(--red); }

  .job-cards { display: flex; flex-direction: column; gap: 12px; min-height: 80px; flex: 1 1 auto; }

  /* The board fills the screen and pins there, so scrolling the page moves
     past the figures and the filters once, and from then on the columns are
     all that scrolls. The figures stay where they were: you scroll them away
     rather than losing them.

     No sticky is involved, and none is needed: giving the board the height of
     the viewport makes the page exactly tall enough to scroll the figures away
     and no further, so it runs out of scroll as the board fills the screen.
     (position:sticky would be dead weight anyway, since the board is the last
     child and so has no travel inside its parent.)

     A definite height (not max-height) is what makes the grid row full-height,
     which is what lets a lane's card list scroll inside it. min-height: 0 is
     what actually lets a flex child scroll instead of growing, and
     overscroll-behavior stops a column hitting its end and dragging the page
     with it. */
  @media (min-width: 861px) {
    /* With the figure cards gone there is nothing left for the page to
       scroll past, so the pipeline sizes itself to the screen and only
       the columns scroll. Flex fill, not a height calc: the chrome above
       the board can change height without this ever being wrong again. */
    #view-pipeline.active { display: flex; flex-direction: column; height: 100vh; }
    .pipeline-board { flex: 1; min-height: 0; grid-template-rows: minmax(0, 1fr); }
    /* Option A, the one-row toolbar: title, count, chips, search and the
       action inline, nothing behind a click. The search grows when
       focused and hands the room back on blur. */
    .pipe-top { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; flex: none; }
    .pipe-top .pipe-title { font-size: 17px; white-space: nowrap; }
    .pipe-top .pipe-filters { margin-bottom: 0; flex: none; }
    .pipe-top .flt-chip { padding: 7px 13px; font-size: 12px; }
    .pipe-top .search-wrap { flex: 1; min-width: 0; max-width: 440px; margin: 0; order: 5; transition: max-width 0.25s; }
    .pipe-top .search-wrap:focus-within { max-width: 620px; }
    .pipe-top .search-wrap input { padding: 9px 14px 9px 40px; font-size: 13px; border-radius: 10px; }
    .pipe-top .search-wrap .ic { left: 13px; }
    .pipe-top .new-job-btn { padding: 9px 16px; font-size: 13px; flex: none; order: 6; }

    .pipe-search-btn, .pipe-filter-btn { display: none; }
    /* Contacts has no refresh button, and it was the refresh that pushed
       the pipeline's search to the right; the contacts search carries
       its own push so both toolbars read the same. */
    #contactsTop .search-wrap { margin-left: auto; }
    /* Refresh belongs beside the thing it refreshes into view, the
       search-and-board side, not the category chips. */
    .pipe-top .pipe-refresh-btn { display: inline-flex; align-items: center; padding: 9px 11px; border: 1.5px solid var(--light-grey); background: var(--warm-white); border-radius: 10px; color: var(--mid-grey); cursor: pointer; flex: none; order: 4; margin-left: auto; }
    .pipe-top .pipe-refresh-btn:hover { border-color: var(--mint); color: var(--forest); }
    /* The scroll lines are the board's only position indicators, so they
       wear the brand instead of disappearing (owner, 14 Aug): sage thumb
       on a faint forest track, for the lanes and for the board row. On
       iPhones the system draws its own overlay bars and ignores this. */
    .job-cards { min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: rgba(74,79,86,0.55) rgba(31,33,36,0.06); padding: 3px 6px 8px; }
    .job-cards::-webkit-scrollbar { width: 8px; }
    .job-cards::-webkit-scrollbar-thumb { background: rgba(74,79,86,0.45); border-radius: 8px; }
    .job-cards::-webkit-scrollbar-thumb:hover { background: rgba(74,79,86,0.7); }
    .job-cards::-webkit-scrollbar-track { background: rgba(31,33,36,0.06); border-radius: 8px; }
    .pipeline-board { scrollbar-color: rgba(74,79,86,0.55) rgba(31,33,36,0.06); }
    .pipeline-board::-webkit-scrollbar { height: 9px; }
    .pipeline-board::-webkit-scrollbar-thumb { background: rgba(74,79,86,0.45); border-radius: 8px; }
    .pipeline-board::-webkit-scrollbar-thumb:hover { background: rgba(74,79,86,0.7); }
    .pipeline-board::-webkit-scrollbar-track { background: rgba(31,33,36,0.06); border-radius: 8px; }
  }
  .job-card { background: var(--warm-white); border-radius: 14px; padding: 16px; border: 1.5px solid var(--light-grey); cursor: pointer; transition: all 0.2s; box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
  .job-card:hover { border-color: var(--sage); transform: translateY(-2px); box-shadow: 0 2px 6px rgba(31,33,36,0.13); }
  .job-card-top { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 9px; }
  .job-card-postcode { font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 700; letter-spacing: 0.6px; color: var(--sage); background: rgba(74,79,86,0.11); padding: 3px 9px; border-radius: 6px; white-space: nowrap; }
  .job-card-total { font-family: var(--font-display); font-size: 15.5px; font-weight: 700; color: var(--forest); margin-left: auto; font-variant-numeric: tabular-nums; }
  .job-card-name { font-family: var(--font-display); font-weight: 600; font-size: 14.5px; color: var(--forest); margin-bottom: 3px; text-transform: capitalize; line-height: 1.3; }
  .job-card-address { font-size: 11.5px; color: var(--mid-grey); line-height: 1.5; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .job-card-meta { display: flex; align-items: center; gap: 6px; font-size: 10.5px; font-weight: 500; color: var(--mid-grey); font-variant-numeric: tabular-nums; letter-spacing: 0.02em; opacity: 0.85; border-top: 1px solid var(--light-grey); margin-top: 10px; padding-top: 9px; }
  .job-card-meta .ic { width: 12px; height: 12px; color: var(--sage); }
  .job-card-meta + .job-card-meta { border-top: none; margin-top: 4px; padding-top: 0; }
  input.invalid, .addr-wrap input.invalid { border-color: var(--red); box-shadow: 0 0 0 3px rgba(168,68,56,0.12); }
  .extra-toggle { display: flex; align-items: center; gap: 10px; margin-top: 18px; font-size: 13.5px; font-weight: 600; color: var(--charcoal); cursor: pointer; text-transform: none; letter-spacing: 0; }
  .extra-toggle input[type="checkbox"] { width: 19px; height: 19px; accent-color: var(--sage); cursor: pointer; }
  /* Appointments as booking rows: a dashed invitation until a booking
     exists, then one line with a status chip and a dots menu of verbs. */
  .booking-list { display: flex; flex-direction: column; gap: 10px; }
  /* The extras block holds several rows plus the add button, so it needs
     the same rhythm inside as the list has between blocks. */
  .booking { display: flex; flex-direction: column; gap: 10px; }
  .bk-add { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; border: 1.5px dashed var(--mint); background: none; border-radius: 10px; padding: 11px 14px; font-size: 13.5px; font-weight: 600; color: var(--sage); cursor: pointer; font-family: var(--font-body); transition: border-color 0.2s, background 0.2s; }
  .bk-add:hover { border-color: var(--sage); background: rgba(74,79,86,0.06); }
  .bk-row { position: relative; display: flex; align-items: center; gap: 10px; border: 1.5px solid var(--light-grey); background: var(--warm-white); border-radius: 10px; padding: 10px 12px; }
  .bk-ic { color: var(--sage); flex: none; }
  .bk-summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px; font-weight: 600; color: var(--charcoal); }
  .bk-chip { flex: none; font-size: 11.5px; font-weight: 700; padding: 3px 11px; border-radius: 999px; background: rgba(74,79,86,0.14); color: #2e5539; }
  .bk-chip-red { background: rgba(168,68,56,0.12); color: var(--red); }
  .bk-cancelled { background: var(--cream); }
  .bk-cancelled .bk-summary { color: var(--mid-grey); font-weight: 500; text-decoration: line-through; }
  .bk-cancelled .bk-ic { color: var(--mid-grey); opacity: 0.6; }
  .bk-dots { flex: none; display: flex; border: none; background: none; padding: 5px; border-radius: 7px; color: var(--mid-grey); cursor: pointer; }
  .bk-dots:hover { background: var(--light-grey); color: var(--charcoal); }
  .bk-menu { position: absolute; top: calc(100% + 4px); right: 0; z-index: 30; background: #fff; border: 1.5px solid var(--light-grey); border-radius: 10px; box-shadow: var(--shadow); min-width: 200px; overflow: hidden; }
  .bk-menu button { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; background: none; border: none; border-top: 1px solid var(--cream); padding: 11px 14px; font-size: 13px; font-family: var(--font-body); color: var(--charcoal); cursor: pointer; }
  .bk-menu button:first-child { border-top: none; }
  .bk-menu button:hover { background: var(--cream); }
  .bk-menu .bk-mi-red { color: var(--red); }
  .bk-menu .bk-mi-green { color: var(--sage); }
  .bk-editor { border: 1.5px solid var(--sage); background: var(--warm-white); border-radius: 10px; padding: 14px; }
  .bk-ed-title { font-size: 13px; font-weight: 700; color: var(--forest); margin-bottom: 10px; }
  .bk-ed-type { margin-bottom: 10px; }
  .bk-ed-type label { display: block; font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--mid-grey); margin-bottom: 5px; }
  .bk-ed-row { display: flex; gap: 10px; align-items: stretch; }
  /* The whole date field is the tap target: the browser's grey indicator
     icon is hidden, our gold calendar icon draws in its place, and a click
     anywhere in the field opens the picker (showPicker in app.js). */
  .bk-date-wrap { position: relative; flex: 1; min-width: 0; display: flex; }
  .bk-date-wrap input[type="date"] { width: 100%; min-width: 0; min-height: 48px; -webkit-appearance: none; appearance: none; background: #fff; cursor: pointer; padding-right: 42px; font-weight: 600; color: var(--charcoal); }
  .bk-date-wrap input[type="date"]:hover { border-color: var(--sage); }
  .bk-date-wrap input[type="date"]::-webkit-calendar-picker-indicator { display: none; }
  .bk-date-btn { position: absolute; right: 0; top: 0; bottom: 0; width: 48px; display: flex; align-items: center; justify-content: center; background: none; border: none; border-left: 1px solid rgba(107,107,107,0.18); border-radius: 0 10px 10px 0; color: var(--forest); cursor: pointer; }
  .bk-date-btn:hover { background: rgba(74,79,86,0.08); }
  /* The branded calendar popover (desktop; phones keep the native wheel). */
  .bk-cal { position: absolute; top: calc(100% + 6px); left: 0; z-index: 40; width: 276px; background: var(--warm-white); border: 1.5px solid var(--light-grey); border-radius: 12px; box-shadow: var(--shadow-lg); padding: 12px; }
  .bk-cal-head { display: flex; align-items: center; gap: 4px; margin-bottom: 8px; }
  .bk-cal-title { flex: 1; font-size: 13.5px; font-weight: 700; color: var(--forest); padding-left: 4px; }
  .bk-cal-nav { display: flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: none; background: none; border-radius: 8px; color: var(--mid-grey); cursor: pointer; }
  .bk-cal-nav:hover { background: var(--cream); color: var(--charcoal); }
  .bk-cal-left { transform: rotate(90deg); }
  .bk-cal-right { transform: rotate(-90deg); }
  .bk-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
  .bk-cal-dow { text-align: center; font-size: 11px; font-weight: 700; color: var(--mid-grey); padding: 4px 0 6px; }
  .bk-cal-day { height: 32px; border: none; background: none; border-radius: 8px; font-size: 12.5px; font-family: var(--font-body); color: var(--charcoal); cursor: pointer; }
  .bk-cal-day:hover { background: var(--cream); }
  .bk-cal-day.out { color: #b9b5ac; }
  .bk-cal-day.today { box-shadow: inset 0 0 0 1.5px var(--gold); }
  .bk-cal-day.sel { background: var(--sage); color: #fff; font-weight: 700; }
  .bk-cal-foot { display: flex; justify-content: space-between; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--light-grey); }
  .bk-cal-clear, .bk-cal-today { border: none; background: none; font-size: 12.5px; font-weight: 600; font-family: var(--font-body); cursor: pointer; padding: 4px 6px; border-radius: 6px; }
  .bk-cal-clear { color: var(--mid-grey); }
  .bk-cal-clear:hover { color: var(--charcoal); }
  .bk-cal-today { color: var(--sage); }
  .bk-cal-today:hover { background: var(--cream); }
  .bk-seg { display: inline-flex; border: 1.5px solid var(--light-grey); border-radius: 10px; overflow: hidden; flex: none; }
  .bk-seg-btn { border: none; background: #fff; color: var(--mid-grey); font-size: 13px; font-weight: 600; font-family: var(--font-body); padding: 0 18px; cursor: pointer; transition: background 0.15s, color 0.15s; }
  .bk-seg-btn.active { background: var(--sage); color: #fff; }
  .bk-ed-actions { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
  .bk-done { background: var(--forest); color: var(--cream); border: none; border-radius: 8px; padding: 9px 24px; font-size: 13px; font-weight: 600; font-family: var(--font-body); cursor: pointer; }
  .bk-done:hover { background: var(--forest-2); }
  .bk-cancel { background: none; border: none; color: var(--mid-grey); font-size: 13px; font-family: var(--font-body); cursor: pointer; padding: 9px 10px; }
  .bk-cancel:hover { color: var(--charcoal); }
  .job-card.dragging { opacity: 0.45; transform: rotate(2deg); }
  .job-cards.drag-over { background: rgba(74,79,86,0.08); outline: 2px dashed var(--sage); outline-offset: -2px; border-radius: 10px; }
  /* The line showing exactly where a dragged card will land. */
  .drop-marker { height: 3px; border-radius: 3px; background: var(--sage); flex: none; margin: -4px 0; box-shadow: 0 0 0 3px rgba(74,79,86,0.18); }
  .col-more { width: 100%; padding: 10px; border: 1.5px dashed var(--light-grey); border-radius: 10px; background: transparent; font-family: var(--font-body); font-size: 11.5px; font-weight: 600; color: var(--mid-grey); cursor: pointer; flex: none; }
  .col-more:hover { border-color: var(--sage); color: var(--forest); }

  /* Grows with the lane so an empty stage is a full-height target to aim at. */
  .empty-col { flex: 1 1 auto; display: flex; align-items: center; justify-content: center; text-align: center; padding: 16px 8px; color: #cfc9c0; font-size: 11.5px; border: 1.5px dashed var(--light-grey); border-radius: 10px; }

  /* ── COMING SOON PAGES ─────────────────────────────── */
  .soon-card { background: var(--warm-white); border: 1px solid var(--light-grey); border-radius: 18px; padding: 64px 32px; text-align: center; box-shadow: var(--shadow); }
  .soon-card .ic.lg { color: var(--mint); margin-bottom: 18px; }
  .soon-card h3 { font-family: var(--font-display); font-size: 19px; font-weight: 700; color: var(--forest); margin-bottom: 10px; }
  .soon-card p { font-size: 14px; color: var(--mid-grey); max-width: 440px; margin: 0 auto; line-height: 1.65; }
  .soon-badge { display: inline-block; margin-top: 20px; padding: 6px 16px; border-radius: 20px; background: rgba(163,50,54,0.14); color: #97781f; font-size: 11px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; }

  /* ── FORM ──────────────────────────────────────────── */
  .form-back { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding-right: 8px; margin-bottom: 10px; cursor: pointer; color: var(--sage); font-weight: 600; font-size: 14px; }
  .form-back:hover { color: var(--forest); }

  /* fill-mode deliberately not "both": a retained transform turns every card
     into its own stacking layer, which painted later cards over the address
     dropdown overflowing the card above. */
  .card { background: var(--warm-white); border-radius: 16px; padding: 28px 32px; margin-bottom: 20px; box-shadow: var(--shadow); border: 1px solid var(--light-grey); animation: fadeUp 0.35s ease; }
  @keyframes fadeUp { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
  .card-title { font-family: var(--font-display); font-size: 16px; font-weight: 700; color: var(--forest); margin-bottom: 20px; padding-bottom: 12px; border-bottom: 2px solid var(--light-grey); display: flex; align-items: center; gap: 10px; }
  .card-title .icon { width: 30px; height: 30px; background: var(--sage); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: white; flex: none; }
  .card-title .icon .ic { width: 15px; height: 15px; }

  .form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
  .form-grid.three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
  /* A grid track is min-content wide by default, and an iOS date input's
     intrinsic width is wider than a phone column, so both are clamped. */
  .form-grid > * { min-width: 0; }
  .field input, .field select, .field textarea { width: 100%; max-width: 100%; min-width: 0; }
  .field input[type="date"], .field input[type="time"] { -webkit-appearance: none; appearance: none; }
  .field.span2 { grid-column: span 2; }
  label { font-size: 12px; font-weight: 600; color: var(--mid-grey); letter-spacing: 0.8px; text-transform: uppercase; }
  input, select, textarea { font-family: var(--font-body); font-size: 14px; padding: 10px 14px; border: 1.5px solid var(--light-grey); border-radius: 10px; background: var(--cream); color: var(--charcoal); transition: border-color 0.2s, box-shadow 0.2s; outline: none; width: 100%; }
  /* background-color, not the background shorthand: the shorthand resets
     background-image, which wiped the person and chevron off the assignee
     boxes the moment they were focused. */
  input:focus, select:focus, textarea:focus { border-color: var(--sage); box-shadow: 0 0 0 3px rgba(74,79,86,0.12); background-color: white; }
  textarea { resize: vertical; min-height: 92px; line-height: 1.5; }
  /* A label long enough to be a sentence is set as one. The uppercase
     treatment stays for the short field labels everywhere else. */
  .label-plain { font-size: 13.5px; font-weight: 650; color: var(--forest); letter-spacing: 0; text-transform: none; }
  .team-sub { font-size: 12px; font-weight: 600; letter-spacing: 0.8px; text-transform: uppercase; color: var(--mid-grey); margin: 18px 0 6px; }
  /* Two backgrounds: the app's chevron on the right, a person on the left for
     the assignee boxes, whose role is the placeholder rather than a label. */
  .select-person { padding-left: 40px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234a4f56' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='12' cy='7' r='4'/%3E%3C/svg%3E"), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234a4f56' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat, no-repeat; background-position: left 13px center, right 13px center; background-size: 16px, 15px; }
  select { -webkit-appearance: none; appearance: none; padding-right: 38px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234a4f56' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 13px center; background-size: 15px; }

  /* Where the job is, on one line. The stages themselves live in a sheet,
     so ten pills no longer take a quarter of a phone screen. */
  /* The panel is the status: the stage colour fills it rather than sitting in
     a pill inside a box. A chevron says it opens; the word said it twice. */
  .status-current { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 58px; padding: 0 16px; border: 0; border-radius: 12px; background: var(--light-grey); color: var(--charcoal); font-family: var(--font-body); font-size: 15px; font-weight: 700; cursor: pointer; text-align: left; transition: filter 0.15s; }
  .status-current:hover { filter: brightness(1.06); }
  .status-current .ic { width: 17px; height: 17px; flex: none; }
  .status-name { min-width: 0; flex: 1; }
  .status-caret { flex: none; opacity: 0.85; }
  /* Fallbacks for the shipped stages; the CSSOM sheet overrides these and
     covers custom stages too. */
  .status-current.measure { background: #b05e3d; color: #fff; }
  .status-current.quoted { background: #44669a; color: #fff; }
  .status-current.deposit { background: var(--gold); color: var(--forest); }
  .status-current.production { background: var(--sage); color: #fff; }
  .status-current.fitting { background: #71519b; color: #fff; }
  .status-current.ordered { background: #2e5e73; color: #fff; }
  .status-current.received { background: #2a7462; color: #fff; }
  .status-current.repair { background: #bf4123; color: #fff; }
  .status-current.complete { background: var(--forest); color: #fff; }
  .status-current.hold { background: var(--red); color: #fff; }


  /* Above the test and unsynced pills, which sit at 999 so they can clear the
     tab bar. A sheet is asking a question, so it covers the passive badges
     rather than being covered by them. */
  /* While the phone keyboard is up, the bottom chrome steps aside: the
     keyboard lays OVER the page, so anything pinned to the screen foot
     sits buried behind it, and none of it is needed mid-typing. The
     !important is deliberate: this state must beat every look variant
     (glass dock, tucked pill, on-job corner circles, pipeline FAB). */
  @media (max-width: 860px) {
    body.keyboard-open .tabbar, body.keyboard-open .dock-pill,
    body.keyboard-open .form-dock, body.keyboard-open .pipe-fab { display: none !important; }
  }
  .sheet-overlay { position: fixed; inset: 0; z-index: 1001; background: rgba(31,33,36,0.42); display: flex; align-items: center; justify-content: center; padding: 16px; animation: sheetIn 0.18s ease; }
  .sheet { width: 100%; max-width: 420px; background: var(--warm-white); border-radius: 18px; box-shadow: var(--shadow-lg); padding: 6px 12px 8px; max-height: 78vh; overflow-y: auto; animation: sheetUp 0.22s cubic-bezier(0.22,0.61,0.36,1); }
  .sheet-row { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 48px; padding: 0 8px; border: 0; border-bottom: 1px solid var(--light-grey); background: none; font-family: var(--font-body); font-size: 14px; font-weight: 600; color: var(--charcoal); cursor: pointer; text-align: left; }
  .sheet-row:last-child { border-bottom: 0; }
  .sheet-row:hover, .sheet-row:focus-visible { background: var(--cream); }
  .sheet-row.on { color: var(--forest); }
  .sheet-name { flex: 1; min-width: 0; }
  .sheet-now { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--sage); }
  .stage-dot { width: 11px; height: 11px; border-radius: 50%; background: var(--mid-grey); flex: none; }
  @keyframes sheetIn { from { opacity: 0; } to { opacity: 1; } }
  @keyframes sheetUp { from { transform: translateY(8px) scale(0.98); } to { transform: none; } }

  /* Collapsible form sections: the heading stays, the body folds away and the
     choice is remembered on this device. Customer Details carries no toggle on
     purpose — its name and address stay visible so nobody types into the wrong
     job. Folding is visual only; collapsed fields still save. */
  .sec-head { cursor: pointer; }
  .sec-caret { color: var(--mid-grey); margin-left: auto; flex: none; transition: transform 0.15s; }
  .totals-title .sec-caret { color: var(--mint); }
  /* The caret sits hard right like every other section. Fold all only appears
     from two blinds up, and when it is there it takes the auto margin and the
     caret simply follows it; when it is hidden the caret keeps it, instead of
     collapsing back against the heading. */
  .blind-title-row .sec-caret { margin-left: auto; }
  .blind-title-row .fold-all-btn:not(.is-hidden) ~ .sec-caret { margin-left: 0; }
  .card.collapsed > :not(.card-title) { display: none; }
  .totals-card.collapsed > :not(.totals-title) { display: none; }
  .card.collapsed .card-title,
  .totals-card.collapsed .totals-title { margin-bottom: 0; padding-bottom: 0; border-bottom: none; }
  .collapsed .sec-caret { transform: rotate(-90deg); }

  /* Save and Documents, two ways round. On desktop the sticky left rail
     carries both, so nothing floats. On a phone the rail is gone, so the pair
     floats over the form and glides down into the page when the foot of the
     form comes into view. */
  .rail-save { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; margin-top: 12px; padding: 10px; background: var(--gold); color: var(--forest); border: none; border-radius: 10px; font-family: var(--font-body); font-size: 13px; font-weight: 700; cursor: pointer; transition: all 0.2s; }
  .rail-save:hover { background: var(--gold-deep); }
  .rail-docs-wrap { margin-top: 8px; }
  .rail-docs { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; padding: 10px; background: var(--forest); color: var(--cream); border: none; border-radius: 10px; font-family: var(--font-body); font-size: 13px; font-weight: 700; cursor: pointer; transition: all 0.2s; }
  .rail-docs:hover { background: var(--forest-2); }
  /* Discard is the quiet undo beside the loud actions: outline only, and
     it wears the dock family's colours (owner's call, 15 Aug) so gold Save
     stays the only loud thing. Present only while there is genuinely
     something to throw away. */
  .rail-discard { display: none; align-items: center; justify-content: center; gap: 8px; width: 100%; margin-top: 8px; padding: 9px; background: transparent; color: var(--forest); border: 1px solid var(--light-grey); border-radius: 10px; font-family: var(--font-body); font-size: 12.5px; font-weight: 700; cursor: pointer; }
  .rail-discard:hover { background: #ecebe8; border-color: #cfc9c0; }
  body.job-dirty .rail-discard { display: flex; }
  .dock-save:hover { background: var(--gold-deep); }
  .dock-docs:hover { background: var(--forest-2); }
  .dock-discard:hover { background: #ecebe8; }
  /* The rail sits near the top of the page, so its menu drops down rather
     than opening upward off the screen the way the phone dock's does. */
  .rail-docs-wrap .doc-menu { top: calc(100% + 8px); bottom: auto; }

  /* Fixed, not sticky: Save and Documents stay in one place the whole way
     down a job rather than settling at the foot, so the thumb never has to
     look for them. Phones only — the desktop rail carries both. */
  .form-dock { display: none; position: fixed; left: 12px; right: 12px; z-index: 130; gap: 10px; align-items: center; }
  .dock-btn { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 14px 20px; border: none; border-radius: 999px; font-family: var(--font-body); font-size: 14px; font-weight: 700; cursor: pointer; box-shadow: var(--shadow-lg); transition: box-shadow 0.32s, border-radius 0.32s; }
  .dock-save { background: var(--gold); color: var(--forest); }
  .dock-docs { background: var(--forest); color: var(--cream); }
  .dock-discard { display: none; background: var(--cream); color: var(--forest); border: 1px solid #d6d2cb; }
  body.job-dirty .dock-discard { display: flex; }
  .form-dock .doc-menu-wrap { min-width: 0; }
  .form-dock .doc-menu { left: 0; right: 0; }

  /* Filled in, the customer card reads as details you can act on. The Edit
     button puts the boxes back. An empty job never enters read mode, so a
     new job still opens straight onto the form. */
  /* Blank between opening the job and knowing whether it reads or edits, so
     the edit boxes never flash up on their way to the read view. */
  #sec-customer.resolving .cust-form, #sec-customer.resolving .cust-read { display: none; }
  #sec-customer.reading .cust-form { display: none; }
  #sec-customer:not(.reading) .cust-read { display: none; }
  .cust-edit { margin-left: auto; border: 1.5px solid var(--light-grey); background: none; border-radius: 999px; padding: 6px 15px; font-family: var(--font-body); font-size: 12px; font-weight: 700; color: var(--sage); cursor: pointer; }
  .cust-edit:hover { border-color: var(--sage); background: var(--cream); }
  /* Offered, never applied. A repeat customer typed by phone or email brings
     the rest of their details with them only when someone taps for it. */
  .cust-match { display: flex; align-items: center; gap: 12px; padding: 10px 12px; margin-bottom: 16px; border: 1.5px solid var(--mint); border-radius: 12px; background: rgba(74,79,86,0.06); }
  .cm-ic { width: 34px; height: 34px; border-radius: 10px; flex: none; display: flex; align-items: center; justify-content: center; background: var(--sage); color: #fff; }
  .cm-ic .ic { width: 16px; height: 16px; }
  .cm-txt { min-width: 0; font-size: 13.5px; line-height: 1.35; color: var(--charcoal); }
  .cm-txt strong { font-weight: 700; color: var(--forest); }
  .cm-txt small { display: block; font-size: 11.5px; color: var(--mid-grey); }
  .cm-use { margin-left: auto; flex: none; min-height: 40px; padding: 0 16px; border: 0; border-radius: 10px; background: var(--sage); color: #fff; font-family: var(--font-body); font-size: 12.5px; font-weight: 700; cursor: pointer; transition: background 0.15s; }
  .cm-use:hover { background: var(--forest); }
  @media (max-width: 560px) {
    .cust-match { flex-wrap: wrap; }
    .cm-use { margin-left: 0; width: 100%; }
  }

  .cust-read { display: flex; flex-direction: column; gap: 14px; }
  .cust-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
  .cust-name { font-family: var(--font-display); font-size: 23px; font-weight: 700; color: var(--forest); letter-spacing: -0.015em; text-transform: capitalize; line-height: 1.15; }
  /* Same pill the pipeline cards use, so a postcode reads the same wherever
     it appears. It is what the calendar, the route and the fitter go by. */
  .cust-pc { margin-left: auto; font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 700; letter-spacing: 0.8px; color: var(--sage); background: rgba(74,79,86,0.11); padding: 5px 11px; border-radius: 7px; white-space: nowrap; }
  .cust-pc.none { color: var(--mid-grey); background: var(--light-grey); font-weight: 600; letter-spacing: 0.3px; }
  /* The card stays sage on both job types except the postcode, which goes gold
     on a commercial job exactly as it already does on the pipeline cards. One
     mark, in the place the eye already looks for it, rather than tinting the
     whole card and splitting the palette across it. */
  #sec-customer.commercial-job .cust-pc { color: #97781f; background: rgba(163,50,54,0.16); }
  .cust-lines { display: flex; flex-direction: column; border: 1.5px solid var(--light-grey); border-radius: 12px; overflow: hidden; }
  .cust-act { display: flex; align-items: center; background: var(--warm-white); color: var(--charcoal); }
  .cust-act + .cust-act { border-top: 1.5px solid var(--light-grey); }
  .cust-hit { display: flex; align-items: center; gap: 13px; flex: 1; min-width: 0; min-height: 58px; padding: 10px 4px 10px 14px; text-decoration: none; color: inherit; transition: background 0.15s; }
  .cust-hit:hover { background: var(--cream); }
  /* Copy sits at the edge, quiet until you reach for it. 44px square so it is
     a real target next to the verb rather than a decoration on it. */
  .cust-copy { width: 44px; min-height: 44px; margin-right: 6px; border: 0; background: none; border-radius: 10px; color: var(--mid-grey); cursor: pointer; display: flex; align-items: center; justify-content: center; flex: none; transition: background 0.15s, color 0.15s; }
  .cust-copy:hover { background: var(--cream); color: var(--sage); }
  .cust-copy:focus-visible { outline: 2px solid var(--sage); outline-offset: -2px; }
  /* A fact row has no link and no copy, so it keeps the padding itself. */
  .cust-act.static { padding: 10px 14px; gap: 13px; min-height: 58px; }
  /* One treatment for all three, rather than a different colour each. */
  .cust-ic { width: 34px; height: 34px; border-radius: 10px; display: flex; align-items: center; justify-content: center; flex: none; background: rgba(74,79,86,0.11); color: var(--sage); }
  .cust-ic .ic { width: 16px; height: 16px; }
  .cust-txt { min-width: 0; font-size: 14px; font-weight: 650; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; }
  .cust-txt small { display: block; font-size: 11.5px; font-weight: 400; color: var(--mid-grey); }
  /* No verb on an action row: the sage tile marks it as tappable and the tag
     column belongs to the fact rows, which do need naming. */
  /* A row that is a fact rather than an action: no hover, no verb, and a
     quiet label so it never reads as something to press. */
  .cust-act.static { cursor: default; }
  .cust-act.static .cust-ic { background: var(--light-grey); color: var(--mid-grey); }
  .cust-tag { margin-left: auto; font-size: 11px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--mid-grey); white-space: nowrap; flex: none; }
  /* Done is the way back out of the boxes, in the place Edit was. */
  .cust-edit.done { color: var(--forest); border-color: var(--sage); background: rgba(74,79,86,0.09); }

  .blind-list { display: flex; flex-direction: column; gap: 16px; }
  .blind-item { background: var(--cream); border: 1.5px solid var(--light-grey); border-radius: 12px; padding: 20px; transition: border-color 0.2s; }
  .blind-item:hover { border-color: var(--mint); }
  .blind-item-header { display: flex; align-items: center; justify-content: space-between; min-height: 44px; margin-bottom: 12px; cursor: pointer; gap: 10px; }
  /* Saved blinds fold to one line: number, product, price. The summary and
     caret only show folded; the whole header is the tap target. */
  .blind-summary { display: none; font-size: 13.5px; font-weight: 600; color: var(--charcoal); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .blind-caret { color: var(--mid-grey); flex: none; transition: transform 0.15s; }
  .blind-item .csp-utility-08 { min-width: 0; flex: 1; }
  .blind-item.collapsed > :not(.blind-item-header) { display: none; }
  .blind-item.collapsed .blind-item-header { margin-bottom: 0; }
  .blind-item.collapsed .blind-summary { display: block; }
  .blind-item.collapsed [id^="btitle-"] { display: none; }
  .blind-item.collapsed .blind-caret { transform: rotate(-90deg); }
  .blind-item.collapsed .blind-price-tag span { display: none; }
  .blind-title-row { display: flex; align-items: center; }
  .fold-all-btn { margin-left: auto; border: 1.5px solid var(--light-grey); background: none; border-radius: 8px; padding: 6px 14px; font-size: 12.5px; font-weight: 600; font-family: var(--font-body); color: var(--mid-grey); cursor: pointer; text-transform: none; letter-spacing: 0; }
  .fold-all-btn:hover { color: var(--forest); border-color: var(--sage); }
  @media (max-width: 860px) {
    /* Phone rows: the product name gets the room the caret took, and can
       wrap to a second line instead of vanishing into an ellipsis. */
    .blind-item.collapsed .blind-caret { display: none; }
    .blind-item.collapsed .blind-summary { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.35; }
    .blind-item.collapsed .csp-utility-09 { gap: 8px; }
  }
  /* flex: none — in the folded header the badge is a flex child of a
     squeezed row, and without it the circle shrinks into an oval. */
  .blind-number { background: var(--forest); color: white; font-size: 11px; font-weight: 600; width: 26px; height: 26px; min-width: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex: none; }
  .blind-price-tag { font-family: var(--font-display); font-size: 16px; color: var(--sage); font-weight: 700; }
  .blind-price-tag span { font-size: 11px; color: var(--mid-grey); font-family: var(--font-body); font-weight: 400; margin-left: 4px; }
  /* 132px so five can share the row when a blind shows product, room, colour,
     width and drop; auto-fit still collapses to four, three or two when a
     product has fewer to show, so every row reaches the edge. */
  .blind-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; align-items: end; }
  /* Extra rows (shape, material, option) sit under the main row and need the
     same breathing space the fabric row already had, or their label collides
     with the controls above. */
  .blind-fields + .blind-fields { margin-top: 12px; }
  /* one-up means one field across the row, but it was only saying so on
     phones, so on a desktop an accessory quantity or a variant picker sat in
     a 1.5fr column with three empty ones after it. */
  .blind-fields.one-up { grid-template-columns: 1fr; }
  /* A charge is three single fields: what it is, how many, and a note. Each
     takes the row rather than huddling at the left edge. */
  .blind-item.is-charge .blind-fields { grid-template-columns: 2fr 1fr; }
  /* field-sizing grows the box with what is typed, which is the only way it
     adjusts on a phone: iOS ignores CSS resize, so the drag handle never
     appears there, and this app cannot set heights from JS under its CSP.
     Browsers without it fall back to a normal box that still drags on
     desktop, which is where it is today. */
  .blind-note { min-height: 46px; max-height: 260px; field-sizing: content; resize: vertical; }
  #notes { field-sizing: content; max-height: 340px; }
  .blind-item.is-charge .blind-fields-row2 { grid-template-columns: 1fr; }
  .blind-fields-row2 { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 12px; }
  /* Sidebar plus rail plus padding takes roughly 490px before the blind row
     gets any width at all, so four columns need a genuinely wide window. */
  @media (min-width: 861px) and (max-width: 1180px) {
    .blind-fields { grid-template-columns: 1fr 1fr; }
    .blind-fields-row2 { grid-template-columns: 1fr; }
    .blind-fields.mod-row { grid-template-columns: 1fr 1fr; align-items: center; }
  }
  .remove-btn { background: none; border: 1.5px solid var(--light-grey); color: var(--mid-grey); width: 40px; height: 40px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; }
  .remove-btn:hover { background: #fde8e8; border-color: var(--red); color: var(--red); }
  .surcharge-note { font-size: 11px; color: #97781f; margin-top: 8px; font-weight: 500; display: none; }
  .surcharge-note.visible { display: block; }
  .add-btn { width: 100%; padding: 14px; background: transparent; border: 2px dashed var(--mint); border-radius: 12px; color: var(--sage); font-family: var(--font-body); font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; margin-top: 8px; }
  .add-btn:hover { background: rgba(74,79,86,0.06); border-color: var(--sage); }

  /* Emphasis comes from the forest green. It used to also come from a heavier
     shadow and a taller header, which said the same thing three times and left
     the card visibly bigger than every other one, most obviously when folded.
     Same padding, same shadow and the same header height as .card now. */
  .totals-card { background: var(--forest); border: 1px solid transparent; border-radius: 16px; padding: 28px 32px; margin-bottom: 20px; box-shadow: var(--shadow); }
  .totals-title { font-family: var(--font-display); color: var(--mint); font-size: 15px; font-weight: 600; min-height: 30px; margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid rgba(184,179,170,0.2); display: flex; align-items: center; gap: 9px; }
  .totals-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; color: rgba(255,255,255,0.7); font-size: 14px; }
  .totals-row.main { color: white; font-size: 26px; font-family: var(--font-display); font-weight: 700; padding-top: 14px; margin-top: 6px; border-top: 1px solid rgba(184,179,170,0.2); }
  .totals-row.main .label { color: var(--mint); font-size: 14px; font-family: var(--font-body); font-weight: 500; }
  /* The balance is worked out, not entered, so it reads as a figure in the
     same family as the total rather than a box you can type in. */
  .totals-row.hold-row { color: var(--mint); font-size: 13px; }
  .totals-row.balance { color: white; font-size: 19px; font-family: var(--font-display); font-weight: 700; margin-top: 14px; padding-top: 14px; border-top: 1px solid rgba(184,179,170,0.2); }
  /* Credit is good news, not a warning, so it takes the mint rather than the
     red a negative number would have implied. */
  .totals-row.credit { color: var(--mint); font-size: 15px; font-family: var(--font-display); font-weight: 700; padding-top: 4px; }
  .totals-row.credit .label { font-family: var(--font-body); font-weight: 500; font-size: 13px; }
  .totals-row.balance .label { color: var(--mint); font-size: 13px; font-family: var(--font-body); font-weight: 500; }
  /* ── Payments ────────────────────────────────────────────────
     A list, not two boxes. Each row is shaped like a job card: the method
     and the figure lead, a hairline, then the quiet line with the menu
     tucked bottom right, so nothing crowds the corner on a phone. */
  .pay-head { display: flex; justify-content: space-between; align-items: baseline; margin-top: 12px; padding-top: 13px; border-top: 1px solid rgba(184,179,170,0.2); }
  .pay-head-title { color: var(--mint); font-size: 12px; letter-spacing: 0.8px; text-transform: uppercase; font-weight: 600; }
  .pay-head-count { color: rgba(255,255,255,0.5); font-size: 12px; }
  .pay-list { display: flex; flex-direction: column; gap: 7px; margin-top: 9px; }
  .pay-list:empty { display: none; }
  .pay-item { position: relative; display: flex; flex-direction: column; background: rgba(255,255,255,0.06); border: 1.5px solid rgba(184,179,170,0.18); border-radius: 12px; padding: 12px 9px 6px 14px; }
  .pay-item-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding-right: 3px; }
  .pay-item-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; border-top: 1px solid rgba(184,179,170,0.18); margin-top: 11px; padding-top: 3px; }
  .pay-method { display: inline-flex; align-items: center; gap: 6px; background: rgba(184,179,170,0.16); color: var(--mint); border-radius: 999px; padding: 4px 12px 4px 9px; font-size: 11.5px; font-weight: 700; letter-spacing: 0.3px; white-space: nowrap; }
  .pay-method .ic { width: 13px; height: 13px; }
  .pay-method.is-blank { background: rgba(255,255,255,0.07); color: rgba(255,255,255,0.5); font-weight: 500; }
  .pay-amount { margin-left: auto; color: white; font-family: var(--font-display); font-size: 17px; font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums; }
  .pay-meta { color: rgba(255,255,255,0.55); font-size: 12px; font-weight: 500; letter-spacing: 0.02em; font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pay-dots { flex: none; display: flex; align-items: center; justify-content: center; width: 34px; height: 34px; border: none; background: none; border-radius: 9px; color: rgba(255,255,255,0.4); cursor: pointer; }
  .pay-dots .ic { width: 18px; height: 18px; }
  .pay-dots:hover { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.7); }
  /* Voided money stops counting but never disappears: that is what makes the
     history worth trusting. */
  .pay-item.is-void { opacity: 0.55; }
  .pay-item.is-void .pay-amount { text-decoration: line-through; text-decoration-thickness: 1px; }
  .pay-menu { position: absolute; right: 9px; bottom: 44px; z-index: 5; background: var(--warm-white); border: 1px solid var(--light-grey); border-radius: 10px; box-shadow: var(--shadow-lg); padding: 5px; min-width: 148px; }
  .pay-menu button { display: block; width: 100%; text-align: left; border: none; background: none; padding: 11px 14px; border-radius: 7px; font-family: var(--font-body); font-size: 13px; font-weight: 600; color: var(--charcoal); cursor: pointer; }
  .pay-menu button:hover { background: var(--cream); }
  .pay-add { display: flex; align-items: center; justify-content: center; gap: 7px; width: 100%; margin-top: 10px; padding: 14px; background: none; border: 2px dashed rgba(184,179,170,0.38); border-radius: 12px; color: var(--mint); font-family: var(--font-body); font-size: 14px; font-weight: 600; cursor: pointer; white-space: nowrap; transition: border-color 0.2s, background 0.2s; }
  .pay-add:hover { border-color: var(--mint); background: rgba(184,179,170,0.08); }
  .pay-form { margin-top: 10px; padding: 16px 14px; background: rgba(255,255,255,0.05); border: 1.5px solid rgba(184,179,170,0.3); border-radius: 12px; display: flex; flex-direction: column; gap: 14px; }
  /* Amount and date pair, and the tracks are clamped: an iOS date input's
     intrinsic width is wider than half a phone column and would push the
     panel sideways, the same trap .form-grid guards against. */
  .pay-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
  .pay-form-grid > * { min-width: 0; }
  .pay-form-label { display: block; color: var(--mint); font-size: 11px; font-weight: 600; letter-spacing: 0.8px; text-transform: uppercase; margin-bottom: 6px; }
  .pay-segs { display: flex; gap: 6px; }
  .pay-seg { flex: 1; display: flex; align-items: center; justify-content: center; gap: 5px; padding: 12px 6px; border-radius: 10px; background: rgba(255,255,255,0.07); border: 1.5px solid rgba(184,179,170,0.25); color: rgba(255,255,255,0.75); font-family: var(--font-body); font-size: 13px; font-weight: 600; cursor: pointer; white-space: nowrap; transition: background 0.15s, border-color 0.15s, color 0.15s; }
  .pay-seg .ic { width: 14px; height: 14px; }
  /* Choosing is not committing, so a chosen method fills sage. The one gold
     button is the one that actually takes the money. */
  .pay-seg.is-on { background: var(--sage); border-color: var(--sage); color: white; }
  .pay-form-actions { display: flex; gap: 10px; }
  .pay-btn { flex: 1; padding: 14px; border-radius: 12px; font-family: var(--font-body); font-size: 14px; font-weight: 700; cursor: pointer; white-space: nowrap; transition: background 0.2s, border-color 0.2s; }
  .pay-btn.ghost { background: none; border: 2px solid rgba(184,179,170,0.32); color: var(--mint); }
  .pay-btn.go { background: var(--pay-gold); border: 2px solid var(--pay-gold); color: #17210f; }
  .pay-btn.go:hover { background: #b79e58; border-color: #b79e58; }
  .pay-form input.invalid { border-color: var(--red); }
  /* The save-time backstop marks the list itself: the two inputs it used
     to mark are hidden now, so nothing lit up. */
  .pay-list.invalid .pay-item { border-color: var(--red); }
  @media (max-width: 390px) {
    .pay-seg { padding-inline: 4px; gap: 4px; }
    .pay-seg .ic { display: none; }
  }

  /* Kept for the payment form's amount and date fields; the old two-box
     deposit row it was written for is gone. */
  .deposit-field label { color: var(--mint); font-size: 11px; }
  .deposit-field input { background: rgba(255,255,255,0.08); border-color: rgba(184,179,170,0.3); color: white; }
  .deposit-field input:focus { border-color: var(--gold); box-shadow: 0 0 0 3px rgba(163,50,54,0.15); background: rgba(255,255,255,0.12); }
  .deposit-field input::placeholder { color: rgba(255,255,255,0.3); }
  .discount-controls { display: flex; align-items: center; gap: 8px; flex: 1; margin-left: 10px; }
  .discount-controls input, .discount-controls select { background: rgba(255,255,255,0.08); border: 1.5px solid rgba(184,179,170,0.3); color: white; border-radius: 8px; padding: 6px 8px; font-family: var(--font-body); font-size: 13px; outline: none; }
  .discount-controls input { width: 76px; text-align: right; }
  /* Inputs are width:100% globally, so the type picker has to be told not to
     swallow the space the group leaves for the amount on the right. */
  .discount-controls select { width: auto; min-width: 104px; flex: none; }
  .discount-controls input:focus, .discount-controls select:focus { border-color: var(--gold); }
  .discount-controls select option { color: var(--charcoal); }
  #discountShown { color: var(--gold); font-weight: 600; min-width: 70px; text-align: right; margin-left: auto; }

  .sig-canvas { border: 2px solid var(--light-grey); border-radius: 12px; background: white; cursor: crosshair; touch-action: none; width: 100%; height: 160px; display: block; }
  .sig-actions { display: flex; gap: 10px; margin-top: 10px; align-items: center; }
  .sig-clear { display: inline-flex; align-items: center; gap: 7px; padding: 8px 18px; background: transparent; border: 1.5px solid var(--light-grey); border-radius: 8px; font-family: var(--font-body); font-size: 13px; cursor: pointer; color: var(--mid-grey); }
  .sig-clear:hover { border-color: var(--red); color: var(--red); }
  .agree-check { display: flex; align-items: flex-start; gap: 12px; margin-top: 16px; padding: 16px; background: var(--cream); border-radius: 10px; border: 1.5px solid var(--light-grey); }
  .agree-check input[type="checkbox"] { width: 20px; height: 20px; min-width: 20px; margin-top: 2px; accent-color: var(--sage); cursor: pointer; }
  .agree-check label { font-size: 13px; color: var(--charcoal); text-transform: none; letter-spacing: 0; font-weight: 400; line-height: 1.5; cursor: pointer; }
  /* The Appearance switch borrows the agree-check shell but holds a
     two-word name rather than a paragraph: centred against its box, and
     weighted as a label instead of legal copy. */
  .agree-check.dock-pref { align-items: center; margin-top: 0; }
  .agree-check.dock-pref input[type="checkbox"] { margin-top: 0; }
  .agree-check.dock-pref label { font-weight: 600; font-size: 13.5px; }

  .actions { display: flex; gap: 10px; margin-top: 8px; flex-wrap: wrap; align-items: center; }
  .actions button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
  .doc-menu-wrap { position: relative; flex: 1; min-width: 150px; }
  .doc-menu { display: none; position: absolute; bottom: calc(100% + 8px); left: 0; right: 0; background: var(--warm-white); border: 1.5px solid var(--light-grey); border-radius: 14px; box-shadow: var(--shadow-lg); padding: 6px; z-index: 120; }
  .doc-menu.open { display: block; }
  .doc-menu button { display: flex; width: 100%; align-items: center; gap: 10px; padding: 12px 14px; background: none; border: none; border-radius: 9px; font-family: var(--font-body); font-size: 13.5px; font-weight: 600; color: var(--charcoal); cursor: pointer; text-align: left; }
  .doc-menu button:hover { background: var(--cream); color: var(--forest); }
  .doc-menu button .ic { color: var(--sage); }
  .doc-menu-tag { margin-left: auto; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: var(--mid-grey); background: var(--cream); padding: 3px 8px; border-radius: 6px; }
  .delete-link { margin-left: auto; display: inline-flex; align-items: center; gap: 7px; padding: 10px 14px; background: none; border: none; color: var(--red); opacity: 0.75; font-family: var(--font-body); font-size: 13px; font-weight: 600; cursor: pointer; border-radius: 10px; }
  .delete-link:hover { opacity: 1; background: #f6e3e1; }
  @media (max-width: 560px) { .delete-link { margin-left: 0; width: 100%; justify-content: center; } }
  .btn-primary { flex: 1; padding: 14px 16px; background: var(--sage); color: white; border: none; border-radius: 12px; font-family: var(--font-body); font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; min-width: 120px; }
  .btn-primary:hover { background: var(--forest); transform: translateY(-1px); }
  /* Every primary and secondary button behaves the same way when pressed and
     when focused, rather than each one inventing its own. */
  .rail-save, .rail-docs, .rail-discard, .dock-save, .dock-docs, .dock-discard, .btn-save, .btn-primary { transition: background-color 0.15s, transform 0.1s, box-shadow 0.15s; }
  .rail-save:active, .rail-docs:active, .rail-discard:active, .dock-save:active, .dock-docs:active, .dock-discard:active,
  .btn-save:active, .btn-primary:active { transform: translateY(1px); }
  .rail-save:focus-visible, .rail-docs:focus-visible, .rail-discard:focus-visible, .dock-save:focus-visible,
  .dock-docs:focus-visible, .dock-discard:focus-visible, .btn-save:focus-visible, .btn-primary:focus-visible,
  .cust-edit:focus-visible, .status-current:focus-visible { outline: 3px solid var(--sage); outline-offset: 2px; }
  .btn-save { flex: 1; padding: 14px 16px; background: var(--gold); color: var(--forest); border: none; border-radius: 12px; font-family: var(--font-body); font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; min-width: 120px; }
  .btn-save:hover { background: var(--gold-deep); transform: translateY(-1px); }
  .btn-secondary { padding: 14px 16px; background: transparent; color: var(--sage); border: 2px solid var(--sage); border-radius: 12px; font-family: var(--font-body); font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
  .btn-secondary:hover { background: var(--sage); color: white; }
  .btn-invoice { padding: 14px 16px; background: #2e5e73; color: white; border: none; border-radius: 12px; font-family: var(--font-body); font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
  .btn-invoice:hover { background: #244b5c; transform: translateY(-1px); }
  .btn-danger { padding: 14px 16px; background: transparent; color: var(--red); border: 2px solid var(--red); border-radius: 12px; font-family: var(--font-body); font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
  .btn-danger:hover { background: var(--red); color: white; }

  .modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 200; align-items: center; justify-content: center; padding: 20px; }
  .modal-overlay.open { display: flex; }
  /* overscroll-behavior stops a drag that reaches the end of the document
     from handing the scroll to the page behind and dragging blank space into
     view. body.doc-shown holds the page still for the same reason. */
  .modal { overflow-x: hidden; background: white; border-radius: 20px; padding: 40px; max-width: 680px; width: 100%; max-height: 90vh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; position: relative; }
  body.doc-shown { overflow: hidden; }
  /* The job sheet's table is 600px of real columns and has to stay reachable,
     so it scrolls in its own box rather than taking the sheet with it. Never
     during capture: .pdf-capture lays the document out at a fixed 640px, and
     clipping there would cut columns out of the PDF. */
  #docContent:not(.pdf-capture) { overflow-x: auto; }
  #docContent img { height: auto; }
  .modal-close { position: absolute; top: 16px; right: 20px; background: none; border: none; cursor: pointer; color: var(--mid-grey); padding: 4px; }
  .modal-close:hover { color: var(--red); }
  .doc-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 32px; padding-bottom: 20px; border-bottom: 3px solid var(--forest); }
  .doc-company h2 { font-family: var(--font-display); font-size: 20px; font-weight: 700; color: var(--forest); }
  .doc-company p { font-size: 12px; color: var(--mid-grey); margin-top: 3px; }
  .doc-type { font-family: var(--font-display); font-size: 22px; font-weight: 700; color: var(--sage); text-align: right; }
  .doc-type .doc-num { font-size: 12px; color: var(--mid-grey); font-family: var(--font-body); font-weight: 400; margin-top: 4px; }
  .doc-customer { margin-bottom: 24px; }
  .doc-customer h3 { font-size: 11px; font-weight: 600; color: var(--mid-grey); text-transform: uppercase; letter-spacing: 0.8px; margin-bottom: 8px; }
  .doc-customer p { font-size: 14px; color: var(--charcoal); line-height: 1.6; }
  .doc-table { width: 100%; border-collapse: collapse; margin-bottom: 24px; }
  .doc-table th { background: var(--forest); color: white; padding: 10px 14px; text-align: left; font-size: 12px; font-weight: 600; letter-spacing: 0.5px; }
  .doc-table td { padding: 12px 14px; border-bottom: 1px solid var(--light-grey); font-size: 14px; }
  .doc-totals { margin-left: auto; width: 260px; margin-bottom: 24px; }
  .doc-total-row { display: flex; justify-content: space-between; padding: 8px 0; font-size: 14px; border-bottom: 1px solid var(--light-grey); }
  .doc-total-row.grand { font-family: var(--font-display); font-size: 18px; font-weight: 700; color: var(--forest); border-bottom: none; padding-top: 12px; }
  .doc-terms { padding-top: 20px; border-top: 1px solid var(--light-grey); font-size: 12px; color: var(--mid-grey); line-height: 1.6; }
  .doc-paid { display: inline-block; margin-top: 8px; padding: 4px 16px; border: 2.5px solid var(--sage); border-radius: 8px; color: var(--sage); font-size: 14px; font-weight: 800; letter-spacing: 2px; }
  .doc-sig { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--light-grey); }
  /* max-height alone let a wide signature scale to whatever width kept that
     height: 390px inside a 303px sheet on a phone, which is the blank space a
     finger could drag the quote, invoice and part invoice sideways into. The
     job sheet never showed it because it carries no signature. */
  .doc-sig img { border: 1px solid var(--light-grey); border-radius: 8px; max-height: 80px; max-width: 100%; }
  /* Long documents flow over pages, but a page break must never slice a
     product row, the money block or the signature in half. html2pdf's css
     mode and the browser's own print both honour these. */
  .doc-table tr, .doc-total-row, .doc-sig, .doc-terms { page-break-inside: avoid; break-inside: avoid; }
  .doc-table thead { display: table-header-group; }
  .doc-actions { display: flex; gap: 12px; margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--light-grey); flex-wrap: wrap; }
  .doc-actions button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }

  .toast { position: fixed; bottom: 24px; right: 24px; background: var(--forest); color: white; padding: 14px 22px; border-radius: 12px; font-size: 14px; font-weight: 500; box-shadow: var(--shadow-lg); z-index: 1000; transform: translateY(80px); opacity: 0; transition: all 0.3s; }
  .toast.show { transform: translateY(0); opacity: 1; }
  .contact-card { background: var(--warm-white); border: 1.5px solid var(--light-grey); border-radius: 14px; padding: 16px 18px; margin-bottom: 12px; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,0.05); transition: border-color 0.2s; }
  .contact-card:hover { border-color: var(--sage); }
  .contact-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
  .contact-name { font-family: var(--font-display); font-weight: 600; font-size: 15px; color: var(--forest); text-transform: capitalize; }
  .contact-meta { font-size: 12px; color: var(--mid-grey); margin-top: 7px; }
  .contact-stats { text-align: right; flex: none; }
  .contact-total { font-family: var(--font-display); font-weight: 700; font-size: 15px; color: var(--forest); }
  .contact-count { font-size: 11.5px; color: var(--sage); font-weight: 600; }
  .contact-jobs { display: none; margin-top: 12px; border-top: 1px solid var(--light-grey); padding-top: 4px; }
  .contact-card.open .contact-jobs { display: block; }
  .contact-job { display: flex; align-items: center; gap: 12px; padding: 9px 4px; font-size: 13px; color: var(--charcoal); text-decoration: none; border-bottom: 1px solid var(--light-grey); }
  .contact-job:last-child { border-bottom: none; }
  .contact-job:hover .cj-total { color: var(--sage); }
  .list-tools { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: -4px 0 14px; color: var(--mid-grey); font-size: 12.5px; }
  .page-size-label { display: flex; align-items: center; gap: 7px; white-space: nowrap; }
  /* Page size is a two-way segmented toggle: the filled side is the
     current choice, so it needs no label at all. */
  /* Toggle and page controls sit together, centred by the row — the same
     at every width. */
  .page-size-seg { display: inline-flex; flex: none; margin-right: 6px; border: 1.5px solid var(--light-grey); border-radius: 9px; overflow: hidden; }
  .page-size-seg .seg-opt { padding: 8px 13px; border: none; background: var(--warm-white); color: var(--mid-grey); font-family: var(--font-body); font-size: 12.5px; font-weight: 600; cursor: pointer; }
  .page-size-seg .seg-opt + .seg-opt { border-left: 1.5px solid var(--light-grey); }
  .page-size-seg .seg-opt:hover:not(.is-on) { color: var(--sage); }
  .page-size-seg .seg-opt.is-on { background: var(--forest); color: var(--warm-white); cursor: default; }
  .pager { display: flex; align-items: center; justify-content: center; gap: 12px; margin: 18px 0 4px; flex-wrap: wrap; }
  .pager[hidden] { display: none !important; }
  /* Icon-only: a chevron each way (the shared down-chevron sprite, rotated),
     with the direction spoken by the button's aria-label. */
  .pager-btn { display: inline-flex; align-items: center; justify-content: center; padding: 8px 11px; border: 1.5px solid var(--light-grey); border-radius: 9px; background: var(--warm-white); color: var(--forest); cursor: pointer; }
  .pager-left { transform: rotate(90deg); }
  .pager-right { transform: rotate(-90deg); }
  .pager-btn:hover:not(:disabled) { border-color: var(--sage); color: var(--sage); }
  .pager-btn:disabled { opacity: 0.42; cursor: default; }
  .pager-info { min-width: 92px; text-align: center; color: var(--mid-grey); font-size: 12px; font-weight: 600; }
  .cj-status { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; padding: 3px 9px; border-radius: 6px; flex: none; }
  .cj-status.measure { background: #f7e8e0; color: #b05e3d; }
  .cj-status.quoted { background: #e7edf5; color: #44669a; }
  .cj-status.deposit { background: #f5edd6; color: #97781f; }
  .cj-status.production { background: #e4efe7; color: #4a4f56; }
  .cj-status.fitting { background: #ece5f4; color: #71519b; }
  .cj-status.ordered { background: #e0ecf1; color: #2e5e73; }
  .cj-status.received { background: #dff0ec; color: #2a7462; }
  .cj-status.repair { background: #fae3d7; color: #bf4123; }
  .cj-status.complete { background: #e6e4e0; color: var(--forest); }
  .cj-status.hold { background: #f6e3e1; color: var(--red); }
  .cj-total { margin-left: auto; font-weight: 600; flex: none; }
  /* Activity card is collapsed by default; history loads on first open */
  #sec-activity .act-toggle { cursor: pointer; margin-bottom: 0; border-bottom: none; padding-bottom: 0; user-select: none; }
  #sec-activity .act-chevron { margin-left: auto; color: var(--mid-grey); transition: transform 0.2s; }
  #sec-activity.open .act-chevron { transform: rotate(180deg); }
  #sec-activity #activityList { display: none; }
  #sec-activity.open #activityList { display: block; margin-top: 16px; border-top: 2px solid var(--light-grey); padding-top: 8px; }
  #sec-activity #activityPager { display: none; }
  #sec-activity.open #activityPager { display: flex; }
  #sec-activity.open #activityPager[hidden] { display: none; }
  .act-row { display: flex; align-items: baseline; gap: 10px; padding: 8px 0; font-size: 13px; border-bottom: 1px solid var(--light-grey); }
  .act-row:last-child { border-bottom: none; }
  .act-who { font-weight: 600; color: var(--forest); flex: none; }
  .act-when { margin-left: auto; color: var(--mid-grey); font-size: 11.5px; white-space: nowrap; flex: none; }
  .env-badge { display: none; position: fixed; bottom: 14px; left: 50%; transform: translateX(-50%); z-index: 999; background: #b8860b; color: white; font-size: 11px; font-weight: 700; letter-spacing: 1px; padding: 6px 16px; border-radius: 20px; box-shadow: var(--shadow-lg); pointer-events: none; white-space: nowrap; }
  body.env-test .env-badge { display: block; }
  @media (max-width: 860px) { .env-badge { bottom: calc(72px + env(safe-area-inset-bottom)); } }

  /* Unsynced work. Sits above the test badge so the two never overlap, and
     turns amber when the phone has no signal at all. */
  .sync-badge { position: fixed; bottom: 14px; right: 16px; z-index: 999; background: var(--forest); color: var(--cream); font-size: 11.5px; font-weight: 600; padding: 7px 15px; border-radius: 20px; box-shadow: var(--shadow-lg); pointer-events: none; max-width: calc(100vw - 32px); }
  .sync-badge.offline { background: #b8860b; color: white; }
  /* The offline badge is the team's only signal that jobs are saved on
     the device, so it anchors above whatever nav chrome is active, the
     same sweep the test badge and toast already got (review finding: it
     kept its old offsets and overlapped both). */
  @media (max-width: 860px) { .sync-badge { bottom: calc(72px + env(safe-area-inset-bottom)); right: 12px; } }
  body.env-test .sync-badge { bottom: 48px; }
  @media (max-width: 860px) {
    body.env-test .sync-badge { bottom: calc(106px + env(safe-area-inset-bottom)); }
    body.glass-dock .sync-badge { bottom: calc(88px + env(safe-area-inset-bottom)); }
    body.glass-dock.env-test .sync-badge { bottom: calc(122px + env(safe-area-inset-bottom)); }
  }

  .form-layout { display: grid; grid-template-columns: 180px minmax(0,1fr); gap: 20px; align-items: start; }
  .form-rail { position: sticky; top: 24px; background: var(--warm-white); border: 1px solid var(--light-grey); border-radius: 14px; padding: 14px 10px; box-shadow: var(--shadow); }
  .rail-name { font-family: var(--font-display); font-size: 14px; font-weight: 700; color: var(--forest); margin: 0 8px 1px; min-height: 18px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-transform: capitalize; }
  .rail-total { font-size: 12px; color: var(--sage); margin: 0 8px 12px; }
  .rail-link { display: flex; align-items: center; gap: 9px; padding: 8px; border-radius: 8px; font-size: 12.5px; font-weight: 600; color: var(--mid-grey); cursor: pointer; transition: all 0.15s; }
  .rail-link .ic { width: 14px; height: 14px; }
  .rail-link:hover { background: var(--cream); color: var(--forest); }
  .rail-link.active { background: #ecebe8; color: var(--forest); }
  .rail-link .tick { margin-left: auto; color: var(--sage); visibility: hidden; }
  .rail-link .tick .ic { width: 12px; height: 12px; stroke-width: 3; }
  .rail-link.done .tick { visibility: visible; }
  .chip.active { background: #ecebe8; border-color: var(--sage); color: var(--forest); }
  .addr-wrap { position: relative; }
  .addr-list { position: absolute; top: 100%; left: 0; right: 0; background: white; border: 1.5px solid var(--light-grey); border-radius: 12px; margin-top: 4px; z-index: 400; max-height: 320px; overflow-y: auto; box-shadow: var(--shadow-lg); display: none; }
  .addr-list.open { display: block; }
  .addr-item { padding: 10px 14px; cursor: pointer; border-bottom: 1px solid var(--light-grey); }
  .addr-item:last-child { border-bottom: none; }
  .addr-item:hover, .addr-item.sel { background: var(--cream); }
  .addr-main { font-size: 13.5px; font-weight: 600; color: var(--charcoal); }
  .addr-sub { font-size: 12px; color: var(--mid-grey); margin-top: 2px; }
  .addr-note { padding: 11px 14px; font-size: 12.5px; color: var(--mid-grey); background: var(--cream); border-bottom: 1px solid var(--light-grey); }
  .addr-note:last-child { border-bottom: none; }
  .addr-powered { padding: 6px 14px 7px; font-size: 10px; color: #b9b2a6; text-align: right; letter-spacing: 0.03em; }

  /* ── ACCOUNTS / TEAM ───────────────────────────────── */
  .team-btn { padding: 6px 12px; border-radius: 8px; border: 1.5px solid var(--light-grey); background: var(--warm-white); font-family: var(--font-body); font-size: 11.5px; font-weight: 600; color: var(--mid-grey); cursor: pointer; transition: all 0.15s; }
  .team-btn:hover { border-color: var(--sage); color: var(--forest); }
  .team-btn.danger:hover { border-color: var(--red); color: var(--red); }
  #teamList .act-row { flex-wrap: wrap; align-items: center; }
  /* On a phone a team member reads as a small stacked record, name then
     email then last sign-in, with the buttons on their own full-width row,
     rather than five things fighting for one line. */
  @media (max-width: 768px) {
    #teamList .act-row { display: grid; grid-template-columns: 1fr; gap: 4px; padding: 14px 0; }
    #teamList .act-who { font-size: 14.5px; }
    #teamList .csp-utility-07 { font-size: 12.5px; word-break: break-word; }
    #teamList .act-when { margin-left: 0; font-size: 11.5px; white-space: normal; }
    #teamList .csp-utility-13 { display: flex; gap: 8px; margin-top: 8px; }
    #teamList .team-btn { flex: 1; padding: 10px 12px; font-size: 12.5px; }
  }

  /* Settings sub-navigation */
  /* Tabs swipe sideways rather than wrapping onto a second line, the same
     carousel treatment as the pipeline filter chips. */
  .settings-tabs { display: flex; gap: 6px; margin-bottom: 22px; border-bottom: 1.5px solid var(--light-grey); flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; scrollbar-width: none; max-width: 100%; }
  .settings-tabs::-webkit-scrollbar { display: none; }
  .set-tab { display: inline-flex; align-items: center; gap: 7px; flex: none; white-space: nowrap; padding: 10px 16px; font-family: var(--font-body); font-size: 13.5px; font-weight: 600; color: var(--mid-grey); text-decoration: none; border-bottom: 2.5px solid transparent; margin-bottom: -1.5px; transition: color 0.15s, border-color 0.15s; }
  .set-tab:hover { color: var(--forest); }
  .set-tab.active { color: var(--forest); border-bottom-color: var(--sage); }
  .set-tab .ic { color: currentColor; }
  .admin-only-note .card-title .icon { background: var(--light-grey); color: var(--mid-grey); }

  /* Version panel */
  .ver-now { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding-bottom: 16px; border-bottom: 1px solid var(--light-grey); flex-wrap: wrap; }
  .ver-number { font-family: var(--font-display); font-size: 21px; font-weight: 700; color: var(--forest); }
  .ver-meta { font-size: 12.5px; color: var(--mid-grey); margin-top: 3px; }
  .ver-env { font-size: 10.5px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; padding: 5px 12px; border-radius: 20px; background: #e6e4e0; color: var(--forest); }
  .ver-env.test { background: #f5edd6; color: #97781f; }
  .ver-history-title { font-size: 11px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--mid-grey); margin: 18px 0 10px; }
  .ver-history { display: flex; flex-direction: column; gap: 16px; }
  .ver-row-head { display: flex; align-items: center; gap: 10px; }
  .ver-tag { font-family: var(--font-display); font-size: 12.5px; font-weight: 700; color: var(--mid-grey); background: var(--cream); border: 1px solid var(--light-grey); border-radius: 20px; padding: 2px 10px; }
  .ver-tag.current { color: var(--forest); background: #e6e4e0; border-color: transparent; }
  .ver-date { font-size: 12px; color: var(--mid-grey); }
  .ver-notes { margin: 8px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
  .ver-notes li { font-size: 13px; color: var(--charcoal); line-height: 1.5; }

  /* Trash (Settings) — deleted jobs waiting to be restored or destroyed */
  .trash-list { display: flex; flex-direction: column; }
  .trash-row { display: flex; align-items: center; gap: 12px; padding: 11px 2px; border-bottom: 1px solid var(--light-grey); }
  .trash-row:last-child { border-bottom: none; }
  .trash-main { min-width: 0; flex: 1; }
  .trash-name { font-weight: 600; color: var(--forest); font-size: 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .trash-pill { font-family: var(--font-body); font-size: 10.5px; font-weight: 700; letter-spacing: 0.5px; color: var(--gold); border: 1px solid var(--gold); border-radius: 20px; padding: 1px 8px; }
  .trash-meta { font-size: 12px; color: var(--mid-grey); margin-top: 2px; }
  .trash-actions { display: flex; gap: 8px; flex: none; }
  .trash-empty { font-size: 13px; color: var(--mid-grey); margin: 0; }
  /* On a phone the buttons drop under the job rather than squeezing it. */
  @media (max-width: 768px) {
    .trash-row { display: grid; grid-template-columns: 1fr; gap: 8px; padding: 14px 0; }
    .trash-actions { width: 100%; }
    .trash-actions .team-btn { flex: 1; padding: 10px 12px; font-size: 12.5px; }
  }

  /* Pipeline stage manager (Settings, admin only) */
  .stage-hint { font-size: 12.5px; color: var(--mid-grey); margin: 0 0 14px; }
  .stage-list { display: flex; flex-direction: column; }
  .stage-row { display: flex; align-items: center; gap: 10px; padding: 9px 4px; border-bottom: 1px solid var(--light-grey); background: var(--warm-white); transition: background 0.15s; }
  .stage-row.dragging { opacity: 0.45; }
  .stage-row.drag-over { background: var(--cream); box-shadow: inset 0 2px 0 var(--sage); }
  .stage-grip { color: var(--light-grey); font-size: 13px; letter-spacing: -3px; cursor: grab; user-select: none; padding: 2px 4px 2px 0; }
  .stage-row:hover .stage-grip { color: var(--mid-grey); }
  .stage-arrows { display: flex; flex-direction: column; gap: 1px; }
  .stage-arrow { border: none; background: transparent; padding: 0 3px; cursor: pointer; color: var(--mid-grey); line-height: 0; }
  .stage-arrow .ic { width: 11px; height: 11px; transform: rotate(180deg); }
  .stage-arrow.down .ic { transform: none; }
  .stage-arrow:disabled { opacity: 0.25; cursor: default; }
  .stage-swatch { width: 24px; height: 24px; min-width: 24px; border-radius: 7px; border: 2px solid transparent; cursor: pointer; padding: 0; }
  .stage-name { flex: 1; min-width: 90px; max-width: 220px; border: 1.5px solid transparent; border-radius: 8px; background: transparent; font-family: var(--font-body); font-size: 13.5px; font-weight: 600; color: var(--charcoal); padding: 6px 8px; }
  .stage-name:hover { border-color: var(--light-grey); }
  .stage-name:focus { border-color: var(--sage); background: white; outline: none; }
  .stage-count { font-size: 11.5px; color: var(--mid-grey); white-space: nowrap; }
  .stage-note { display: inline-flex; align-items: center; gap: 4px; font-size: 10.5px; color: var(--mid-grey); white-space: nowrap; }
  .stage-note .ic { width: 12px; height: 12px; }
  .stage-tag { font-size: 10px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase; color: var(--mid-grey); border: 1px solid var(--light-grey); border-radius: 20px; padding: 2px 8px; }
  .stage-delete { border: none; background: transparent; padding: 4px; cursor: pointer; color: var(--mid-grey); line-height: 0; border-radius: 6px; }
  .stage-delete:hover:not(:disabled) { color: var(--red); background: var(--cream); }
  .stage-delete:disabled { opacity: 0.3; cursor: not-allowed; }
  .stage-iconbtn { width: 30px; height: 28px; min-width: 30px; display: inline-flex; align-items: center; justify-content: center; border: 1.5px solid var(--light-grey); border-radius: 8px; background: var(--warm-white); color: var(--mid-grey); cursor: pointer; padding: 0; }
  .stage-iconbtn:hover { border-color: var(--sage); color: var(--forest); }
  .stage-noicon { font-size: 13px; font-weight: 700; line-height: 1; }
  .stage-bucketbtn { width: 30px; height: 28px; min-width: 30px; display: inline-flex; align-items: center; justify-content: center; border: 1.5px solid var(--light-grey); border-radius: 8px; background: var(--warm-white); cursor: pointer; padding: 0; }
  .stage-bucketbtn:hover { border-color: var(--sage); }
  .stage-bucketbtn.bk-none { color: #c9c3b4; }
  .stage-bucketbtn.bk-booked { color: #71519b; border-color: #d9cfe8; }
  .stage-bucketbtn.bk-goods { color: #2e5e73; border-color: #c8dbe4; }
  .stage-bucketbtn.bk-holding { color: #a84438; border-color: #ecd0cc; }
  .stage-buckets { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 12px 4px 14px 46px; border-bottom: 1px solid var(--light-grey); }
  .stage-buckets-hint { flex-basis: 100%; font-size: 11.5px; color: var(--mid-grey); }
  .stage-bucketopt { padding: 5px 11px; border: 1.5px solid var(--light-grey); border-radius: 999px; background: var(--warm-white); color: var(--charcoal); font-size: 12px; font-weight: 600; cursor: pointer; transition: border-color 0.12s, background 0.12s; }
  .stage-bucketopt:hover { border-color: var(--sage); background: var(--cream); }
  .stage-bucketopt.bk-booked.picked { background: #ece5f4; border-color: #71519b; color: #71519b; }
  .stage-bucketopt.bk-goods.picked { background: #e0ecf1; border-color: #2e5e73; color: #2e5e73; }
  .stage-bucketopt.bk-holding.picked { background: #f6e3e1; border-color: #a84438; color: #a84438; }
  .stage-bucketopt.bk-none.picked { background: var(--cream); border-color: var(--forest); color: var(--forest); }
  .stage-icons { display: flex; flex-wrap: wrap; gap: 6px; padding: 12px 4px 14px 46px; border-bottom: 1px solid var(--light-grey); }
  .stage-iconopt { width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; border: 1.5px solid var(--light-grey); border-radius: 9px; background: var(--warm-white); color: var(--charcoal); cursor: pointer; padding: 0; transition: border-color 0.12s, background 0.12s; }
  .stage-iconopt:hover { border-color: var(--sage); background: var(--cream); }
  .stage-iconopt.picked { border-color: var(--forest); background: var(--cream); color: var(--forest); }
  .stage-palette { display: flex; flex-wrap: wrap; gap: 8px; padding: 12px 4px 14px 46px; border-bottom: 1px solid var(--light-grey); }
  .stage-dot { width: 26px; height: 26px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; padding: 0; transition: transform 0.12s; }
  .stage-dot:hover { transform: scale(1.12); }
  .stage-dot.picked { outline: 2px solid var(--charcoal); outline-offset: 2px; }
  .stage-actions { display: flex; align-items: center; gap: 10px; margin-top: 14px; flex-wrap: wrap; }
  .stage-actions .btn-secondary { display: inline-flex; align-items: center; gap: 6px; }
  .stage-save { flex: 0 0 auto; min-width: 0; padding: 10px 18px; }
  .stage-error { font-size: 12px; color: var(--red); margin: 8px 0 0; min-height: 1em; }

  /* ── ANALYTICS ─────────────────────────────────────── */
  .ana-filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
  .flt-date-label { font-size: 12px; font-weight: 600; color: var(--mid-grey); }
  .flt-chip { flex: 0 0 auto; padding: 8px 15px; border-radius: 18px; border: 1.5px solid var(--light-grey); background: var(--warm-white); font-family: var(--font-body); font-size: 12.5px; font-weight: 600; color: var(--mid-grey); cursor: pointer; white-space: nowrap; transition: all 0.15s; }
  .flt-chip:hover { border-color: var(--mint); color: var(--forest); }
  .flt-chip.active { background: #ecebe8; border-color: var(--sage); color: var(--forest); }
  .flt-custom { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--mid-grey); margin-left: auto; }
  .flt-custom input { width: 138px; padding: 7px 10px; border: 1.5px solid var(--light-grey); border-radius: 10px; font-family: var(--font-body); font-size: 12.5px; color: var(--charcoal); background: var(--warm-white); outline: none; }
  .flt-custom input:focus { border-color: var(--sage); }
  /* The analytics range controls fold behind one Filter button on a phone,
     the same pattern as the pipeline and contacts toolbars. */
  .ana-filter-btn { display: none; }
  @media (max-width: 860px) {
    .ana-filter-btn { display: inline-flex; align-items: center; gap: 6px; padding: 8px 11px; border: 1.5px solid var(--light-grey); background: var(--warm-white); border-radius: 10px; font-family: var(--font-body); font-size: 12px; font-weight: 600; color: var(--mid-grey); cursor: pointer; }
    .ana-filters.filters-open .ana-filter-btn { border-color: var(--sage); color: var(--forest); background: #ecebe8; }
    .ana-filters .ana-chips, .ana-filters .flt-custom { display: none; }
    .ana-filters.filters-open .ana-chips { display: flex; flex-wrap: wrap; width: 100%; }
    .ana-filters.filters-open .flt-custom { display: grid; grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr); gap: 8px; align-items: center; width: 100%; margin-left: 0; }
    .ana-filters.filters-open .flt-custom input { width: 100%; }
    .ana-filters { display: flex; flex-wrap: wrap; gap: 8px; }
  }

  .ana-card { background: var(--warm-white); border: 1px solid var(--light-grey); border-radius: 14px; padding: 22px; box-shadow: var(--shadow); margin-bottom: 16px; }
  .ana-card-title { font-family: var(--font-display); font-size: 15px; font-weight: 700; color: var(--forest); }
  .ana-card-sub { font-size: 12px; color: var(--mid-grey); margin: 2px 0 18px; }
  /* The pair stretch to one height (owner, 25 Aug): two cards of
     different lengths sitting inline read as untidy, not informative. */
  .ana-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
  .ana-grid .ana-card { margin-bottom: 0; }

  .ana-chart { position: relative; height: 248px; }
  .ana-grid-line { position: absolute; left: 44px; right: 0; height: 1px; background: var(--light-grey); }
  .ana-tick { position: absolute; left: 0; width: 38px; text-align: right; font-size: 10.5px; color: var(--mid-grey); transform: translateY(-50%); font-variant-numeric: tabular-nums; }
  .ana-cols { position: absolute; left: 44px; right: 0; top: 0; bottom: 0; display: flex; align-items: flex-end; }
  .ana-band { flex: 1 0 34px; min-width: 34px; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; position: relative; cursor: default; outline: none; border-radius: 8px; }
  .ana-band:hover, .ana-band:focus-visible { background: rgba(74,79,86,0.06); }
  .ana-bar { width: min(24px, 68%); background: var(--sage); border-radius: 4px 4px 0 0; min-height: 0; transition: filter 0.15s; }
  .ana-band:hover .ana-bar, .ana-band:focus-visible .ana-bar { filter: brightness(1.12); }
  .ana-band .peak { position: absolute; left: 50%; transform: translateX(-50%); padding-bottom: 3px; font-size: 11px; font-weight: 700; color: var(--forest); white-space: nowrap; }
  .ana-x { position: absolute; top: 100%; left: 50%; transform: translateX(-50%); padding-top: 6px; font-size: 10.5px; color: var(--mid-grey); white-space: nowrap; }
  .ana-chart-wrap { padding: 22px 0 26px; overflow-x: auto; }
  /* The tip floats above whatever hosts it, so it never covers the panel
     being read; chart bands keep the inside-top spot, since a band is a
     tall transparent column with nothing to cover. */
  .ana-tip { position: absolute; z-index: 300; bottom: calc(100% + 6px); top: auto; left: 50%; transform: translateX(-50%); width: max-content; max-width: 190px; background: var(--forest); color: white; padding: 9px 13px; border-radius: 10px; font-size: 12px; line-height: 1.5; pointer-events: none; box-shadow: var(--shadow-lg); display: none; }
  .ana-band .ana-tip { top: 8px; bottom: auto; }
  .ana-tip.is-visible { display: block; }
  .ana-tip strong { font-size: 13.5px; }
  /* v1.6.0 · the live money section, sparkline stat cards, the stacked
     taken/owed chart and the previous-period ghost. Matt throughout: flat
     fills, no gradients, the only translucency is the 12% sparkline area. */
  /* Analytics wears the same title size as the pipeline and contacts
     toolbars (owner's call, 15 Aug); Settings keeps the old large head. */
  #view-analytics .page-title { font-size: 17px; }
  #view-analytics .page-head { margin-bottom: 16px; }
  /* Any panel carrying a data-tip hosts the shared tooltip, which is
     absolutely positioned, so each host anchors it itself. */
  .stat-card, .money-leg, .money-total, .ana-row { position: relative; }
  .money-leg, .money-total, .ana-row, .stat-card { outline: none; }
  .money-leg:hover, .money-leg:focus-visible, .ana-row:hover, .ana-row:focus-visible { background: rgba(74,79,86,0.06); border-radius: 8px; }
  .money-live .money-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 14px; }
  .money-total { font-family: var(--font-display); font-size: 26px; font-weight: 700; color: var(--forest); white-space: nowrap; }
  .money-bar { display: flex; gap: 2px; height: 14px; border-radius: 7px; overflow: hidden; margin-bottom: 12px; }
  .money-seg { min-width: 8px; }
  .money-seg-fill { flex: 1; }
  .seg-booked { background: #71519b; }
  .seg-goods { background: #2e5e73; }
  .seg-holding { background: #a84438; }
  /* Tidy aligned rows rather than a ragged wrapped line (owner, 15 Aug):
     name on the left, money on the right, two columns where the width
     allows. */
  .money-legend { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 2px 22px; font-size: 12px; color: var(--charcoal); }
  .money-leg { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 4px 6px; }
  .money-leg-name { display: inline-flex; align-items: center; gap: 7px; min-width: 0; }
  .money-leg-val { white-space: nowrap; color: var(--mid-grey); }
  .money-leg-val strong { color: var(--charcoal); }
  .money-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
  /* ── Money as a flow (owner picked 20 Aug 2026) ────────────────
     Three panels, money moving toward the till: Stuck -> Waiting ->
     In reach. The dark panel is the answer to "what can I get my
     hands on this week"; a fat left panel is a jam you can see. */
  .money-flow { display: grid; grid-template-columns: minmax(0, 0.85fr) 26px minmax(0, 1fr) 26px minmax(0, 1.1fr); align-items: stretch; }
  .mf-panel { display: flex; flex-direction: column; gap: 8px; padding: 14px; border-radius: 12px; min-width: 0; background: var(--warm-white); border: 1px solid var(--light-grey); }
  .mf-title { font-family: var(--font-display); font-size: 11.5px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--sage); }
  .mf-figure { font-family: var(--font-display); font-size: 24px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; color: var(--forest); }
  .mf-bar { height: 8px; border-radius: 4px; margin-bottom: 0; }
  .mf-rows { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; }
  /* Each stage row is a real button through to its column on the board. */
  .mf-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; width: 100%; background: none; border: 0; font: inherit; color: inherit; text-align: left; padding: 3px 5px; margin: 0 -5px; border-radius: 8px; cursor: pointer; }
  .mf-row:hover, .mf-row:focus-visible { background: rgba(74,79,86,0.08); }
  .mf-row-name { display: inline-flex; align-items: center; gap: 7px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .mf-row-val { white-space: nowrap; font-variant-numeric: tabular-nums; display: inline-flex; align-items: center; gap: 3px; }
  .mf-row-val svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; flex: none; }
  .mf-rows, .mf-row-val { color: var(--mid-grey); }
  .mf-row-name { color: var(--charcoal); }
  /* The answer panel leads by type alone: a bigger figure, nothing louder. */
  .mf-reach .mf-figure { font-size: 28px; }
  .mf-empty .mf-figure { font-size: 20px; color: var(--mid-grey); }
  .mf-arrow { display: flex; align-items: center; justify-content: center; }
  .mf-arrow svg { width: 15px; height: 15px; fill: none; stroke: #c4bfb2; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex: none; }
  /* Jack's total line: the three boxes added together, at the end where he
     asked for it (owner review, 29 Aug 2026). */
  .mf-total { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--light-grey); }
  .mf-total-label { font-family: var(--font-display); font-size: 11.5px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--sage); }
  @media (max-width: 700px) {
    /* The flow stands upright on a phone: the answer on top, money
       feeding UP into it, so nothing is lost by stacking. */
    .money-flow { display: flex; flex-direction: column-reverse; gap: 0; }
    .mf-arrow { padding: 6px 0; }
    .mf-arrow svg { transform: rotate(-90deg); }
  }
  .stat-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
  .stat-top > div { min-width: 0; }
  .stat-spark { flex: 0 1 90px; width: auto; min-width: 0; height: 28px; margin-top: 6px; }
  .delta-chip { display: inline-block; margin-left: 7px; padding: 1px 8px; border-radius: 999px; font-size: 10.5px; font-weight: 700; vertical-align: 1px; }
  .delta-chip.good { background: #e4efe7; color: #2f5e42; }
  .delta-chip.bad { background: #f6e3e1; color: #a84438; }
  .ana-head-row { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; flex-wrap: wrap; }
  .ana-hero-label { font-size: 12px; color: var(--mid-grey); }
  .ana-hero { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; margin: 1px 0; }
  .ana-hero-val { font-family: var(--font-display); font-size: 30px; font-weight: 700; color: var(--forest); }
  .ana-hero .delta-chip { vertical-align: baseline; }
  .ana-legend { display: flex; gap: 14px; font-size: 11.5px; color: var(--charcoal); margin: 6px 0 0; flex-wrap: wrap; }
  .ana-leg { display: inline-flex; align-items: center; gap: 5px; }
  .ana-sw { width: 10px; height: 10px; border-radius: 2px; flex: none; }
  .ana-sw.taken { background: var(--sage); }
  .ana-sw.owed { background: #b8b3aa; }
  .ana-sw.prevline { width: 14px; height: 0; border-top: 2px dashed #b4ad9c; border-radius: 0; background: none; }
  .ana-bar-taken { width: min(24px, 68%); background: var(--sage); min-height: 0; }
  .ana-bar-owed { width: min(24px, 68%); background: #b8b3aa; border-radius: 4px 4px 0 0; min-height: 0; border-bottom: 2px solid var(--warm-white); }
  .ana-bar-owed[data-height-pct="0"] { border-bottom: none; }
  .ana-band.solo .ana-bar-taken { border-radius: 4px 4px 0 0; }
  .ana-band:hover .ana-bar-taken, .ana-band:focus-visible .ana-bar-taken,
  .ana-band:hover .ana-bar-owed, .ana-band:focus-visible .ana-bar-owed { filter: brightness(1.08); }
  .ana-ghost { position: absolute; left: 44px; right: 0; top: 0; bottom: 0; pointer-events: none; width: calc(100% - 44px); height: 100%; }
  .mch-compare { flex: none; }

  .ana-row { display: grid; grid-template-columns: minmax(120px, 190px) minmax(0,1fr) auto; gap: 12px; align-items: center; padding: 7px 0; }
  .ana-row-label { font-size: 12.5px; font-weight: 500; color: var(--charcoal); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ana-track { height: 14px; border-radius: 0 4px 4px 0; background: var(--sage); min-width: 2px; }
  .ana-row-val { font-size: 12.5px; font-weight: 700; color: var(--forest); white-space: nowrap; font-variant-numeric: tabular-nums; }
  .ana-row-val span { font-weight: 500; color: var(--mid-grey); }
  .ana-table { margin-top: 14px; border-top: 1px solid var(--light-grey); padding-top: 10px; }
  .ana-table summary { font-size: 12px; font-weight: 600; color: var(--mid-grey); cursor: pointer; }
  .ana-table table { width: 100%; border-collapse: collapse; margin-top: 8px; }
  .ana-table th { text-align: left; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--mid-grey); padding: 5px 4px; border-bottom: 1px solid var(--light-grey); }
  .ana-table td { font-size: 12.5px; padding: 6px 4px; border-bottom: 1px solid var(--light-grey); font-variant-numeric: tabular-nums; }
  .ana-table th:last-child, .ana-table td:last-child, .ana-table th.num, .ana-table td.num { text-align: right; }

  @media (max-width: 860px) {
    .sidebar { display: none; }
    .main { margin-left: 0; padding-bottom: 76px; }
    .mobilebar { display: flex; }
    .tabbar { display: flex; }
    /* ── GLASS DOCK (optional, Settings -> Account) ──────────────────
       Everything is scoped behind body.glass-dock, so with the toggle off
       the flat bar keeps its exact previous styling. On, the tab bar
       floats as a rounded glass pill; scrolling down slides it off the
       bottom and shows the dot pill, scrolling up brings it back. */
    body.glass-dock .main { padding-bottom: 88px; }
    /* Jobs carry their own measured clearance on .form-content, sized to
       the corner controls, so the general padding on top of it was a
       double-count: ~180px of dead cream below the last card. (The
       pipeline's matching rule lives with the pinning block at 768px,
       where the whole mechanism now sits together: review finding, the
       header pin and padding zero applied to 860px while the pinned view
       only existed to 768px, breaking 769-860px viewports.) */
    body.on-job .main { padding-bottom: 0; }
    body.glass-dock .tabbar {
      left: 12px; right: 12px; bottom: calc(10px + env(safe-area-inset-bottom));
      border-radius: 30px;
      border: 1px solid rgba(246,244,240,0.15);
      background: rgba(24,26,29,0.8);
      -webkit-backdrop-filter: blur(16px) saturate(1.4);
      backdrop-filter: blur(16px) saturate(1.4);
      padding: 8px 6px;
      transition: transform 0.35s cubic-bezier(0.4,0,0.2,1), opacity 0.3s;
    }
    /* Anything without backdrop-filter stays close to solid, same policy
       as the frosted top bar. */
    @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
      body.glass-dock .tabbar { background: rgba(24,26,29,0.96); }
    }
    body.glass-dock.dock-away .tabbar {
      transform: translateY(calc(100% + 12px + env(safe-area-inset-bottom)));
      opacity: 0;
      pointer-events: none;
    }
    body.glass-dock .dock-pill { display: flex; }
    body.glass-dock.dock-away .dock-pill { opacity: 1; transform: none; pointer-events: auto; }
    /* Pipeline's own corner action, glass look only: the gold circle the
       job screen taught the thumb, in the same spot, riding the same
       choreography. The header plus goes ghost here for the same reason
       it does on a job: one gold object per screen. */
    body.glass-dock.on-pipeline .pipe-fab { display: flex; position: fixed; right: 16px; bottom: calc(84px + env(safe-area-inset-bottom)); z-index: 102; width: 54px; height: 54px; border-radius: 50%; border: none; background: var(--gold); color: var(--forest); align-items: center; justify-content: center; cursor: pointer; box-shadow: var(--shadow-lg); transition: bottom 0.35s cubic-bezier(0.4,0,0.2,1), transform 0.12s; }
    body.glass-dock.on-pipeline .pipe-fab:active { transform: scale(0.9); }
    body.glass-dock.on-pipeline .pipe-fab .ic { width: 21px; height: 21px; }
    body.glass-dock.on-pipeline.dock-away .pipe-fab { bottom: calc(14px + env(safe-area-inset-bottom)); }
    body.glass-dock.on-pipeline .mob-new { background: rgba(246,244,240,0.14); color: var(--cream); }
    /* Behind the preference the top bar's New Job sheds its label: the
       gold circle and its plus say it, and 36px keeps the bar the same
       height it always was. The text node stays for screen readers;
       font-size zero only silences the pixels. */
    body.glass-dock .mob-new { width: 34px; height: 34px; padding: 0; border-radius: 50%; font-size: 0; gap: 0; justify-content: center; }
    body.glass-dock .mob-new .ic.sm { width: 17px; height: 17px; }
    /* On a job the gold belongs to Save alone: two gold circles on one
       screen fought each other (the owner spotted it on the phone), so
       inside a job the header plus goes quiet, a ghost circle in the
       bar's own material, still there, no longer shouting. */
    body.glass-dock.on-job .mob-new { background: rgba(246,244,240,0.14); color: var(--cream); }
    /* The corner controls keep the bottom-right corner: parked above the
       dock while it shows, gliding down into the freed space once it
       tucks. The floating dock's top edge sits higher than the flat bar's,
       so the resting position moves up too, keeping clear air between the
       pair; tucked, button and pill share one baseline, pill centred and
       button right, so the two can never collide. Documents wears the
       dock's own glass so the pair reads as one family; Save stays gold
       because it is the action, not furniture. */
    body.glass-dock .form-dock { transition: bottom 0.35s cubic-bezier(0.4,0,0.2,1); bottom: calc(84px + env(safe-area-inset-bottom)); }
    body.glass-dock.on-job .form-dock { bottom: calc(86px + env(safe-area-inset-bottom)); }
    body.glass-dock.dock-away .form-dock { bottom: calc(14px + env(safe-area-inset-bottom)); }
    body.glass-dock.dock-away.on-job .form-dock { bottom: calc(14px + env(safe-area-inset-bottom)); }
    body.glass-dock .dock-docs {
      background: rgba(24,26,29,0.8);
      -webkit-backdrop-filter: blur(16px) saturate(1.4);
      backdrop-filter: blur(16px) saturate(1.4);
      border: 1px solid rgba(246,244,240,0.15);
    }
    body.glass-dock .dock-discard {
      background: rgba(24,26,29,0.8);
      -webkit-backdrop-filter: blur(16px) saturate(1.4);
      backdrop-filter: blur(16px) saturate(1.4);
      border: 1px solid rgba(246,244,240,0.15);
      color: var(--cream);
    }
    @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
      body.glass-dock .dock-docs, body.glass-dock .dock-discard { background: rgba(24,26,29,0.96); }
    }
    /* The floating passives, the amber test badge and the toast, anchor a
       constant clearance above whatever nav chrome is active instead of
       the fixed offsets that had them drifting around the dock. */
    body.glass-dock .env-badge { bottom: calc(88px + env(safe-area-inset-bottom)); }
    body.glass-dock .toast { bottom: calc(88px + env(safe-area-inset-bottom)); }
    body.glass-dock.env-test .toast { bottom: calc(122px + env(safe-area-inset-bottom)); }
    @media (prefers-reduced-motion: reduce) {
      body.glass-dock .tabbar, body.glass-dock .form-dock, .dock-pill { transition: none; }
    }
    /* Option C on the phone: title and count, then search and filters
       each behind one button that opens its own full-width row. New Job
       already has the header and the board corner, so it stands down. */
    .pipe-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
    .pipe-top .pipe-title { font-size: 19px; flex: 1; white-space: nowrap; min-width: 0; }
    #view-analytics .page-title { font-size: 19px; }
    .pipe-tool { display: inline-flex; align-items: center; gap: 5px; padding: 8px 11px; border: 1.5px solid var(--light-grey); background: var(--warm-white); border-radius: 10px; font-family: var(--font-body); font-size: 12px; font-weight: 600; color: var(--mid-grey); cursor: pointer; flex: none; }
    .pipe-filter-btn.filt-on, .pipe-top.search-open .pipe-search-btn { border-color: var(--sage); color: var(--forest); background: #ecebe8; }
    /* Same grouping on the phone: search, then refresh beside it, then
       Filter on the end. */
    .pipe-search-btn { order: 1; } .pipe-filter-btn { order: 3; }
    /* No refresh button on phones: coming back to the app IS the refresh
       moment, and a visibility listener refetches quietly then. Desktop
       keeps the button, a machine that stays open never "comes back". */
    .pipe-refresh-btn { display: none; }
    /* The panels OVERLAY the board rather than joining the flow: opening
       them used to shove every column down and snap it back on close,
       which read as a bug, not a dropdown. Absolute below the bar, one
       card shadow, no layout shift at all. */
    .pipe-top { position: relative; }
    .pipe-top .search-wrap, .pipe-top .pipe-filters { display: none; position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 60; width: auto; margin: 0; background: var(--warm-white); border: 1.5px solid var(--light-grey); border-radius: 12px; box-shadow: var(--shadow-lg); padding: 8px; }
    .pipe-top.search-open .search-wrap { display: block; }
    .pipe-top.filters-open .pipe-filters { display: flex; gap: 8px; }
    /* 16px is the size at which iOS stops zooming a focused field, the
       same fix the email prompt needed on 13 Aug. Mobile only: desktop
       keeps its smaller field and has no zoom to fight. */
    .search-wrap input { font-size: 16px; }
    .pipe-top .new-job-btn { display: none; }
    /* Contacts keeps its New Customer button on phones as an icon beside the
       search toggle: the top bar's New Job is a different action, and the
       duplicate button under the list is gone (owner, 20 Sep). */
    #contactsTop .new-job-btn { display: inline-flex; padding: 9px 11px; order: 7; }
    #contactsTop .new-job-btn .btn-label { display: none; }
    .pipe-filters { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; max-width: 100%; }
    .pipe-filters::-webkit-scrollbar { display: none; }
    /* A grid item is min-content wide by default, so the form card refused
       to shrink below its widest row and pushed the page off screen. That
       overflow was what made the whole site drag sideways. */
    .form-layout { grid-template-columns: minmax(0, 1fr); }
    .form-layout > * { min-width: 0; }
    .form-rail { display: none; }
    /* Clear of the tab bar, which is fixed at the foot and 61px tall before
       the phone's own safe area is added on top of it. */
    /* Two equal halves, whatever the labels say. A grid is used rather than
       flex because flex sizes the two around their content and the Documents
       button is a nested child, so the pair never came out level. */
    .form-dock { display: grid; grid-template-columns: 1fr 1fr; bottom: calc(73px + env(safe-area-inset-bottom)); }
    .form-dock > * { min-width: 0; }
    .form-dock .dock-docs { width: 100%; }
    /* The dock is out of flow now, so the form ends clear of both it and the
       tab bar: 61 tab bar + 12 + 47 dock + 12. */
    .form-content { padding-bottom: calc(132px + env(safe-area-inset-bottom)); }

    /* ── ON A JOB: the buttons that apply, and nothing else ──────────
       The dock used to be a pill bar floating on top of the tab bar: 47 + 12
       + 61 = 120px of a 375x812 phone, two rows of furniture doing one row's
       work. Now nothing is reserved for it at all. The controls stack in the
       corner and only appear where they mean something, so a job screen
       usually carries ONE button, never a control that cannot do anything.
         new job          Save
         saved, clean     Documents
         saved, editing   Save above Documents
    */
    body.on-job .form-dock {
      display: flex;
      flex-direction: column-reverse;
      align-items: flex-end;
      gap: 10px;
      left: auto;
      right: 14px;
      bottom: calc(75px + env(safe-area-inset-bottom));
      pointer-events: none;
    }
    body.on-job .form-dock > * { pointer-events: auto; width: auto; }
    /* Icon only: with one control on screen at a time there is nothing to
       tell apart, and a circle in the corner takes almost no room. The label
       stays in the DOM for screen readers via aria-label on the button. */
    body.on-job .dock-btn { width: 54px; height: 54px; padding: 0; border-radius: 50%; }
    body.on-job .dock-label { display: none; }
    body.on-job .dock-btn .ic { width: 21px; height: 21px; }
    /* EXACTLY ONE, ALWAYS. Documents needs a job the server has, and opening
       one saves the job first anyway, so offering it mid-edit was offering a
       second way to do what Save does. Unsaved changes get Save. Nothing to
       save gets Documents. There is never a choice to make. */
    body.on-job:not(.job-saved) .doc-menu-wrap,
    body.on-job.job-dirty .doc-menu-wrap { display: none; }
    /* Until the job is fetched there is no version, so it reads as unsaved and
       Save appeared for the length of the request before swapping to Documents.
       Neither shows until the answer is known. */
    body.job-resolving .form-dock { display: none; }
    body.on-job.job-saved:not(.job-dirty) .dock-save { display: none; }
    body.on-job .form-content { padding-bottom: calc(96px + env(safe-area-inset-bottom)); }
    /* The menu hangs off the right edge it is anchored to, not the viewport. */
    body.on-job .form-dock .doc-menu { left: auto; right: 0; min-width: 190px; }

    /* ── THE TOP BAR IS SOLID FOREST, FULL STOP ──────────────────────
       Frost-on-scroll was tried twice on 14 Aug, first for everyone and
       then behind the glass preference, and the owner's final call killed
       it: with the status strip solid, a frosted bar right under it reads
       as a seam rather than a material. The glass belongs at the foot of
       the screen, where the dock floats clear of any solid edge. */
  }
  @media (max-width: 768px) {
    /* Mobile: one line of columns, swiped left-right with snap — never
       stacked rows. A slice of the next column peeks in as the swipe cue. */
    /* The phone scrolls its columns the same way the desktop does: the board
       swipes sideways, each column's card list scrolls down. Two scrollers,
       but on different axes and on different elements, which the browser has
       no trouble telling apart.

       An earlier attempt made the board itself scroll both ways, and that is
       what felt glitchy under a thumb: one element trying to snap sideways and
       scroll down at once leaves every diagonal swipe ambiguous. It also
       clipped the sticky headers against the board's edge.

       Headers need no sticky here at all now. They sit outside the scrolling
       list rather than inside it, so they simply stay where they are. The
       height leaves room for the fixed tab bar. */
    /* The board is PINNED to the screen with fixed insets, not sized by
       viewport arithmetic: 100vh, dvh and env() disagree between Safari,
       the saved-to-home-screen app and Firefox, and the owner's phone
       showed three different bottoms. Fixed top-to-bottom cannot drift,
       the page cannot scroll under it, and the elastic bounce cannot
       tuck the dock. 63px is the mobile bar below the status inset. */
    /* NATURAL GIVE. The pinned frame is itself the vertical scroller,
       with exactly the toolbar's height of travel: the toolbar slides
       away as native scrolling and the board rides up by the same 60px.
       Nothing inside the gesture ever changes size; the dock, pill and
       corner circle are fixed outside the frame, so tucking them cannot
       resize what the thumb is dragging, which is what made the reverted
       lane-tuck feel glitchy. The frame's bottom edge sits 12px above
       the nav chrome, so clearance can never become dead scroll, and a
       lane that reaches its own end hands the remaining pull to the
       frame, which is how the last card clears the dock. */
    #view-pipeline.active { position: fixed; top: calc(63px + env(safe-area-inset-top)); left: 0; right: 0; bottom: 0; display: flex; flex-direction: column; padding: 12px 12px 0; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
    /* iOS ignores overscroll-behavior on the document often enough that
       a drag starting on the fixed header could still rubber-band the
       page, and a WebKit quirk freezes that bounce mid-air: header
       shoved down, cream gap above it, board seemingly stuck. Locking
       the body removes the document from the gesture entirely; every
       pipeline element is already fixed, so nothing else moves. The
       header pin and the main padding zero live HERE, with the pinned
       view they compensate for, so one breakpoint owns the whole
       mechanism. */
    body.on-pipeline { position: fixed; inset: 0; overflow: hidden; overscroll-behavior-y: none; }
    body.on-pipeline .mobilebar { position: fixed; top: 0; left: 0; right: 0; }
    body.on-pipeline .main { padding-bottom: 0; }
    /* The cards keep their full size, the owner's call: the extra jobs
       in view come from the taller board alone, not from squeezing the
       panels themselves. */
    .pipeline-board { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: min(82vw, 260px); grid-auto-rows: minmax(0, 1fr); flex: none; height: 100%; min-height: 0; overflow-x: auto; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; padding-bottom: 12px; }
    /* The frame runs to the screen edge and the clearance lives in the
       lanes instead (owner, 14 Aug): cards visibly slide UNDER the
       frosted dock, the same effect the contacts list has, and the
       padding is what lets the last card scroll back out of the glass. */
    .job-cards { min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 3px 6px calc(81px + env(safe-area-inset-bottom)); }
    body.glass-dock .job-cards { padding-bottom: calc(96px + env(safe-area-inset-bottom)); }
    /* The desktop 280px minimum would override the narrower mobile column. */
    .pipeline-col { min-width: 0; }
    .pipeline-col { scroll-snap-align: start; }
    /* minmax(0,…) on purpose: a bare 1fr refuses to shrink below its
       content, and the sparkline cards were pushing off the screen edge. */
    .pipeline-stats { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    .form-grid { grid-template-columns: 1fr; }
    /* span2 outlives the two-column layout it was written for: grid has to
       satisfy the span, so it invents a second column, and an implicit column
       sizes to its content. That is why the address row stayed two-across on a
       phone with one field squashed against a wider one. */
    .form-grid .field.span2 { grid-column: auto; }
    .form-grid.three { grid-template-columns: 1fr 1fr; }
    /* Desktop keeps all four controls on one line. On a phone the pair of
       short numeric fields stays side by side and anything marked wide (a
       long label or a truncating select) takes the full width. */
    .blind-fields { grid-template-columns: 1fr 1fr; }
    .blind-fields .field.wide,
    .blind-fields .field.solo { grid-column: 1 / -1; }
    .blind-fields.one-up, .blind-fields.mod-row { grid-template-columns: 1fr; }
    /* A charge pairs its two fields on a desktop, but 2fr 1fr on a phone
       leaves the quantity a third of 317px. It stacks here instead. */
    .blind-item.is-charge .blind-fields { grid-template-columns: 1fr; }
    .blind-fields.mod-row { align-items: center; }
    .blind-fields-row2 { grid-template-columns: 1fr; }
    .card { padding: 20px 16px; }
    .view { padding: 20px 12px; }
    .page-title { font-size: 26px; }
    /* Appointments pair up: date beside its time slot. */
    #sec-dates .form-grid { grid-template-columns: 1fr 1fr; }
    /* The extra booking's type is a long select, so it takes the full width
       and its date and time pair underneath. */
    #extraFields { grid-template-columns: 1fr 1fr; }
    #extraFields .field:first-child { grid-column: 1 / -1; }
    /* A date input has to fit half a phone column without clipping
       dd/mm/yyyy and its picker button. */
    /* Anything under 16px makes iOS zoom the page the moment it is focused,
       and it never zooms back out. 16px is the threshold, not a preference. */
    input, select, textarea { font-size: 16px; }
    .field input[type="date"] { padding-left: 10px; padding-right: 6px; font-size: 16px; }
    /* Quote: the two entered amounts pair, the calculated balance gets its
       own line so it reads as the answer rather than a third input. The
       total already separates itself, so the run-up to the amounts is
       tightened on a phone. */
    /* The discount row is the widest thing in the summary; on a phone it has
       to give back its slack or it pushes the page off screen. */
    /* 20px top and bottom to sit level with every other card, but 18px at the
       sides: the other card titles lead with an icon in a filled box that gives
       its own inset, and this one's does not, so its text would otherwise sit
       tighter to the wall than theirs. */
    .totals-card { padding: 20px 18px; }
    .discount-controls { margin-left: 8px; }
    .discount-controls select { min-width: 88px; }
    #discountShown { min-width: 0; }
    .toast { bottom: calc(72px + env(safe-area-inset-bottom)); right: auto; left: 50%; transform: translate(-50%, 80px); border-radius: 24px; padding: 9px 18px; font-size: 12.5px; }
    .toast.show { transform: translate(-50%, 0); }
    /* Only the test build shows the amber pill, so only there does the toast
       step up to clear it. */
    body.env-test .toast { bottom: calc(106px + env(safe-area-inset-bottom)); }

    /* The job form parks its Save/Documents bar in the strip above the tab
       bar, so while that view is open every floating pill steps up over it.
       58px is the bar's height plus its gap. Other views are untouched. */
    body:has(#view-form.active) { --dock-lift: 58px; }
    body:has(#view-form.active) .env-badge,
    body:has(#view-form.active) .sync-badge,
    body:has(#view-form.active) .toast { bottom: calc(84px + var(--dock-lift)); }
    body.env-test:has(#view-form.active) .sync-badge { bottom: calc(118px + var(--dock-lift)); }
    body.env-test:has(#view-form.active) .toast { bottom: calc(132px + var(--dock-lift)); }
    .modal { padding: 24px 16px; }
    .doc-header { flex-direction: column; gap: 12px; }
    .doc-type { text-align: left; }
    .soon-card { padding: 44px 20px; }
    .ana-grid { grid-template-columns: 1fr; }
    .flt-custom { margin-left: 0; width: 100%; }
    .ana-row { grid-template-columns: minmax(90px, 130px) minmax(0,1fr) auto; }
    .list-tools { align-items: flex-start; }
    /* One row on phones: no wrap, tighter buttons, centred like desktop. */
    .pager { gap: 7px; flex-wrap: nowrap; }
    .pager .pager-btn { padding: 7px 9px; }
    .pager .pager-info { min-width: 0; font-size: 11.5px; }
    .page-size-seg .seg-opt { padding: 7px 11px; }
  }
  /* Applied to #docContent only while a PDF is being captured: fixes the
     width below the A4 printable width so device zoom quirks can't clip
     the right edge, with even slack both sides. */
  .pdf-capture { width: 640px !important; max-width: 640px !important; margin: 0 auto !important; }
  /* Where the copy is captured from: in the document so fonts and styles all
     apply, off screen so nobody ever sees it. Not display:none, which would
     give html2canvas nothing to photograph. */
  .pdf-stage { position: fixed; left: -10000px; top: 0; width: 640px; pointer-events: none; }
  .pdf-stage img { height: auto; }
  #printArea { display: none; }
  @media print {
    /* A4 page width is under 768px CSS px, so the mobile media query would
       otherwise restyle the printout (stacked header). Force desktop layout
       and a white page. */
    html, body { background: #fff !important; }
    body > *:not(#printArea) { display: none !important; }
    #printArea { display: block !important; padding: 24px; font-family: sans-serif; background: #fff; }
    #printArea .doc-header { flex-direction: row !important; }
    #printArea .doc-type { text-align: right !important; }
    #printArea .doc-header { display: flex; justify-content: space-between; margin-bottom: 24px; padding-bottom: 16px; border-bottom: 3px solid #1f2124; }
    #printArea .doc-company h2 { font-size: 22px; color: #1f2124; margin-bottom: 4px; }
    #printArea .doc-company p { font-size: 12px; color: #666; margin: 2px 0; }
    #printArea .doc-type { font-size: 24px; color: #4a4f56; text-align: right; }
    #printArea .doc-num { font-size: 12px; color: #666; margin-top: 4px; }
    #printArea .doc-customer { margin-bottom: 20px; }
    #printArea .doc-customer h3 { font-size: 11px; color: #666; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; }
    #printArea .doc-customer p { font-size: 14px; line-height: 1.6; }
    #printArea .doc-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
    #printArea .doc-table th { background: #1f2124; color: white; padding: 10px 14px; text-align: left; font-size: 12px; }
    #printArea .doc-table td { padding: 10px 14px; border-bottom: 1px solid #eee; font-size: 13px; }
    #printArea .doc-totals { margin-left: auto; width: 260px; margin-bottom: 20px; }
    #printArea .doc-total-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #eee; font-size: 14px; }
    #printArea .doc-total-row.grand { font-size: 18px; font-weight: 700; color: #1f2124; border-bottom: none; }
    #printArea .doc-terms { font-size: 11px; color: #666; border-top: 1px solid #eee; padding-top: 14px; line-height: 1.6; }
    #printArea .doc-sig img { max-height: 80px; border: 1px solid #eee; border-radius: 6px; }
    #printArea .doc-actions { display: none !important; }
  }

/* Strict-CSP state and former inline-style utilities. */
.is-hidden { display: none !important; }
.message-success { color: var(--sage); }
.message-error { color: #b3403f; }
.print-visible { display: block !important; }
.csp-utility-01 { background:none; border:none; color:var(--sage); font-family:var(--font-body); font-size:13px; font-weight:600; cursor:pointer; margin-top:14px; text-decoration:underline; }
.csp-utility-02 { background:var(--warm-white); border-radius:16px; padding:36px 32px; max-width:380px; width:100%; text-align:center; box-shadow:0 20px 60px rgba(0,0,0,0.4); }
.csp-utility-03 { border-top:1px solid var(--light-grey); padding-top:18px; }
.csp-utility-04 { color:#b3403f; font-size:13px; margin-top:10px; min-height:16px; }
.csp-utility-05 { color:var(--charcoal); opacity:0.7; font-size:14px; margin-bottom:22px; }
.csp-utility-06 { color:var(--mid-grey); }
.csp-utility-07 { color:var(--mid-grey); overflow:hidden; text-overflow:ellipsis; }
.csp-utility-08 { display:flex; align-items:center; gap:10px; }
.csp-utility-09 { display:flex; align-items:center; gap:12px; }
.csp-utility-10 { display:flex; gap:10px; flex-wrap:wrap; margin-top:16px; }
.csp-utility-11 { display:inline-flex; align-items:center; gap:8px; }
.csp-utility-12 { display:inline-flex; align-items:center; gap:8px; margin-top:14px; }
.csp-utility-13 { flex:none; display:flex; gap:6px; }
.csp-utility-14 { font-size:10px; color:var(--gold); font-weight:700; }
.csp-utility-15 { font-size:10px; color:var(--sage); font-weight:700; }
.csp-utility-16 { font-size:12px; color:#4a4f56; margin-top:10px; }
.csp-utility-17 { font-size:12px; color:var(--mid-grey); font-weight:500; }
.csp-utility-18 { font-size:12px; color:var(--mid-grey); margin-bottom:8px; }
.csp-utility-19 { font-size:12px; color:var(--mid-grey); margin-top:10px; }
.csp-utility-20 { font-size:12px; color:var(--sage); }
.csp-utility-21 { font-size:12px; font-weight:700; text-transform:uppercase; letter-spacing:0.6px; color:var(--mid-grey); margin-bottom:12px; }
.csp-utility-22 { font-size:13.5px; color:var(--charcoal); margin-bottom:14px; }
.csp-utility-23 { font-size:13.5px; color:var(--charcoal); margin-bottom:14px; line-height:1.5; }
.csp-utility-24 { font-size:13px; color:var(--mid-grey); }
.csp-utility-25 { font-size:13px; color:var(--mid-grey); margin-bottom:14px; }
.csp-utility-26 { font-size:13px; color:var(--red); }
.csp-utility-27 { font-size:14px; color:var(--mid-grey); line-height:1.65; margin-bottom:18px; }
.csp-utility-28 { font-size:26px; font-weight:700; color:var(--forest); margin-bottom:4px; }
.csp-utility-29 { font-weight:600; white-space:nowrap; }
.csp-utility-30 { grid-template-columns:1.5fr 1fr; margin-top:12px; }
.csp-utility-31 { margin-bottom:20px; }
.csp-utility-32 { margin-left:auto; }
.csp-utility-33 { margin-top:14px; }
.csp-utility-35 { max-height:80px; border:1px solid #e8e4de; border-radius:8px; }
.csp-utility-36 { max-width:520px; }
.csp-utility-37 { min-width:0; }
.csp-utility-38 { display:flex; position:fixed; inset:0; background:var(--forest); z-index:9999; align-items:center; justify-content:center; padding:20px; }
.csp-utility-39 { text-align:right; }
.csp-utility-40 { text-align:right; font-weight:600; }
.csp-utility-41 { text-transform:uppercase; }
.csp-utility-42 { width:100%; }
.csp-utility-43 { width:100%; padding:14px 16px; background:var(--gold); color:var(--forest); border:none; border-radius:12px; font-size:15px; font-weight:700; cursor:pointer; }
/* 16px, not 15: one px under Apple's threshold and iOS zooms the page
   when the login field focuses, then stays zoomed after signing in. Same
   rule as the search field and the 13 Aug email prompt. */
.csp-utility-44 { width:100%; padding:14px 16px; border:2px solid var(--mint); border-radius:12px; font-size:16px; margin-bottom:10px; }
.csp-utility-45 { width:100%; padding:14px 16px; border:2px solid var(--mint); border-radius:12px; font-size:16px; margin-bottom:12px; }
.csp-utility-46 { width:100%; padding:14px 16px; border:2px solid var(--mint); border-radius:12px; font-size:16px; text-align:center; letter-spacing:2px; margin-bottom:12px; }

#gateError.message-success { color: var(--sage); }
#gateError.message-error { color: #b3403f; }

/* Data-driven analytics dimensions, kept in the external stylesheet. */
[data-height-pct="0"] { height: 0%; }
[data-bottom-pct="0"] { bottom: 0%; }
[data-top-pct="0"] { top: 0%; }
[data-width-pct="0"] { width: 0%; }
[data-height-pct="1"] { height: 1%; }
[data-bottom-pct="1"] { bottom: 1%; }
[data-top-pct="1"] { top: 1%; }
[data-width-pct="1"] { width: 1%; }
[data-height-pct="2"] { height: 2%; }
[data-bottom-pct="2"] { bottom: 2%; }
[data-top-pct="2"] { top: 2%; }
[data-width-pct="2"] { width: 2%; }
[data-height-pct="3"] { height: 3%; }
[data-bottom-pct="3"] { bottom: 3%; }
[data-top-pct="3"] { top: 3%; }
[data-width-pct="3"] { width: 3%; }
[data-height-pct="4"] { height: 4%; }
[data-bottom-pct="4"] { bottom: 4%; }
[data-top-pct="4"] { top: 4%; }
[data-width-pct="4"] { width: 4%; }
[data-height-pct="5"] { height: 5%; }
[data-bottom-pct="5"] { bottom: 5%; }
[data-top-pct="5"] { top: 5%; }
[data-width-pct="5"] { width: 5%; }
[data-height-pct="6"] { height: 6%; }
[data-bottom-pct="6"] { bottom: 6%; }
[data-top-pct="6"] { top: 6%; }
[data-width-pct="6"] { width: 6%; }
[data-height-pct="7"] { height: 7%; }
[data-bottom-pct="7"] { bottom: 7%; }
[data-top-pct="7"] { top: 7%; }
[data-width-pct="7"] { width: 7%; }
[data-height-pct="8"] { height: 8%; }
[data-bottom-pct="8"] { bottom: 8%; }
[data-top-pct="8"] { top: 8%; }
[data-width-pct="8"] { width: 8%; }
[data-height-pct="9"] { height: 9%; }
[data-bottom-pct="9"] { bottom: 9%; }
[data-top-pct="9"] { top: 9%; }
[data-width-pct="9"] { width: 9%; }
[data-height-pct="10"] { height: 10%; }
[data-bottom-pct="10"] { bottom: 10%; }
[data-top-pct="10"] { top: 10%; }
[data-width-pct="10"] { width: 10%; }
[data-height-pct="11"] { height: 11%; }
[data-bottom-pct="11"] { bottom: 11%; }
[data-top-pct="11"] { top: 11%; }
[data-width-pct="11"] { width: 11%; }
[data-height-pct="12"] { height: 12%; }
[data-bottom-pct="12"] { bottom: 12%; }
[data-top-pct="12"] { top: 12%; }
[data-width-pct="12"] { width: 12%; }
[data-height-pct="13"] { height: 13%; }
[data-bottom-pct="13"] { bottom: 13%; }
[data-top-pct="13"] { top: 13%; }
[data-width-pct="13"] { width: 13%; }
[data-height-pct="14"] { height: 14%; }
[data-bottom-pct="14"] { bottom: 14%; }
[data-top-pct="14"] { top: 14%; }
[data-width-pct="14"] { width: 14%; }
[data-height-pct="15"] { height: 15%; }
[data-bottom-pct="15"] { bottom: 15%; }
[data-top-pct="15"] { top: 15%; }
[data-width-pct="15"] { width: 15%; }
[data-height-pct="16"] { height: 16%; }
[data-bottom-pct="16"] { bottom: 16%; }
[data-top-pct="16"] { top: 16%; }
[data-width-pct="16"] { width: 16%; }
[data-height-pct="17"] { height: 17%; }
[data-bottom-pct="17"] { bottom: 17%; }
[data-top-pct="17"] { top: 17%; }
[data-width-pct="17"] { width: 17%; }
[data-height-pct="18"] { height: 18%; }
[data-bottom-pct="18"] { bottom: 18%; }
[data-top-pct="18"] { top: 18%; }
[data-width-pct="18"] { width: 18%; }
[data-height-pct="19"] { height: 19%; }
[data-bottom-pct="19"] { bottom: 19%; }
[data-top-pct="19"] { top: 19%; }
[data-width-pct="19"] { width: 19%; }
[data-height-pct="20"] { height: 20%; }
[data-bottom-pct="20"] { bottom: 20%; }
[data-top-pct="20"] { top: 20%; }
[data-width-pct="20"] { width: 20%; }
[data-height-pct="21"] { height: 21%; }
[data-bottom-pct="21"] { bottom: 21%; }
[data-top-pct="21"] { top: 21%; }
[data-width-pct="21"] { width: 21%; }
[data-height-pct="22"] { height: 22%; }
[data-bottom-pct="22"] { bottom: 22%; }
[data-top-pct="22"] { top: 22%; }
[data-width-pct="22"] { width: 22%; }
[data-height-pct="23"] { height: 23%; }
[data-bottom-pct="23"] { bottom: 23%; }
[data-top-pct="23"] { top: 23%; }
[data-width-pct="23"] { width: 23%; }
[data-height-pct="24"] { height: 24%; }
[data-bottom-pct="24"] { bottom: 24%; }
[data-top-pct="24"] { top: 24%; }
[data-width-pct="24"] { width: 24%; }
[data-height-pct="25"] { height: 25%; }
[data-bottom-pct="25"] { bottom: 25%; }
[data-top-pct="25"] { top: 25%; }
[data-width-pct="25"] { width: 25%; }
[data-height-pct="26"] { height: 26%; }
[data-bottom-pct="26"] { bottom: 26%; }
[data-top-pct="26"] { top: 26%; }
[data-width-pct="26"] { width: 26%; }
[data-height-pct="27"] { height: 27%; }
[data-bottom-pct="27"] { bottom: 27%; }
[data-top-pct="27"] { top: 27%; }
[data-width-pct="27"] { width: 27%; }
[data-height-pct="28"] { height: 28%; }
[data-bottom-pct="28"] { bottom: 28%; }
[data-top-pct="28"] { top: 28%; }
[data-width-pct="28"] { width: 28%; }
[data-height-pct="29"] { height: 29%; }
[data-bottom-pct="29"] { bottom: 29%; }
[data-top-pct="29"] { top: 29%; }
[data-width-pct="29"] { width: 29%; }
[data-height-pct="30"] { height: 30%; }
[data-bottom-pct="30"] { bottom: 30%; }
[data-top-pct="30"] { top: 30%; }
[data-width-pct="30"] { width: 30%; }
[data-height-pct="31"] { height: 31%; }
[data-bottom-pct="31"] { bottom: 31%; }
[data-top-pct="31"] { top: 31%; }
[data-width-pct="31"] { width: 31%; }
[data-height-pct="32"] { height: 32%; }
[data-bottom-pct="32"] { bottom: 32%; }
[data-top-pct="32"] { top: 32%; }
[data-width-pct="32"] { width: 32%; }
[data-height-pct="33"] { height: 33%; }
[data-bottom-pct="33"] { bottom: 33%; }
[data-top-pct="33"] { top: 33%; }
[data-width-pct="33"] { width: 33%; }
[data-height-pct="34"] { height: 34%; }
[data-bottom-pct="34"] { bottom: 34%; }
[data-top-pct="34"] { top: 34%; }
[data-width-pct="34"] { width: 34%; }
[data-height-pct="35"] { height: 35%; }
[data-bottom-pct="35"] { bottom: 35%; }
[data-top-pct="35"] { top: 35%; }
[data-width-pct="35"] { width: 35%; }
[data-height-pct="36"] { height: 36%; }
[data-bottom-pct="36"] { bottom: 36%; }
[data-top-pct="36"] { top: 36%; }
[data-width-pct="36"] { width: 36%; }
[data-height-pct="37"] { height: 37%; }
[data-bottom-pct="37"] { bottom: 37%; }
[data-top-pct="37"] { top: 37%; }
[data-width-pct="37"] { width: 37%; }
[data-height-pct="38"] { height: 38%; }
[data-bottom-pct="38"] { bottom: 38%; }
[data-top-pct="38"] { top: 38%; }
[data-width-pct="38"] { width: 38%; }
[data-height-pct="39"] { height: 39%; }
[data-bottom-pct="39"] { bottom: 39%; }
[data-top-pct="39"] { top: 39%; }
[data-width-pct="39"] { width: 39%; }
[data-height-pct="40"] { height: 40%; }
[data-bottom-pct="40"] { bottom: 40%; }
[data-top-pct="40"] { top: 40%; }
[data-width-pct="40"] { width: 40%; }
[data-height-pct="41"] { height: 41%; }
[data-bottom-pct="41"] { bottom: 41%; }
[data-top-pct="41"] { top: 41%; }
[data-width-pct="41"] { width: 41%; }
[data-height-pct="42"] { height: 42%; }
[data-bottom-pct="42"] { bottom: 42%; }
[data-top-pct="42"] { top: 42%; }
[data-width-pct="42"] { width: 42%; }
[data-height-pct="43"] { height: 43%; }
[data-bottom-pct="43"] { bottom: 43%; }
[data-top-pct="43"] { top: 43%; }
[data-width-pct="43"] { width: 43%; }
[data-height-pct="44"] { height: 44%; }
[data-bottom-pct="44"] { bottom: 44%; }
[data-top-pct="44"] { top: 44%; }
[data-width-pct="44"] { width: 44%; }
[data-height-pct="45"] { height: 45%; }
[data-bottom-pct="45"] { bottom: 45%; }
[data-top-pct="45"] { top: 45%; }
[data-width-pct="45"] { width: 45%; }
[data-height-pct="46"] { height: 46%; }
[data-bottom-pct="46"] { bottom: 46%; }
[data-top-pct="46"] { top: 46%; }
[data-width-pct="46"] { width: 46%; }
[data-height-pct="47"] { height: 47%; }
[data-bottom-pct="47"] { bottom: 47%; }
[data-top-pct="47"] { top: 47%; }
[data-width-pct="47"] { width: 47%; }
[data-height-pct="48"] { height: 48%; }
[data-bottom-pct="48"] { bottom: 48%; }
[data-top-pct="48"] { top: 48%; }
[data-width-pct="48"] { width: 48%; }
[data-height-pct="49"] { height: 49%; }
[data-bottom-pct="49"] { bottom: 49%; }
[data-top-pct="49"] { top: 49%; }
[data-width-pct="49"] { width: 49%; }
[data-height-pct="50"] { height: 50%; }
[data-bottom-pct="50"] { bottom: 50%; }
[data-top-pct="50"] { top: 50%; }
[data-width-pct="50"] { width: 50%; }
[data-height-pct="51"] { height: 51%; }
[data-bottom-pct="51"] { bottom: 51%; }
[data-top-pct="51"] { top: 51%; }
[data-width-pct="51"] { width: 51%; }
[data-height-pct="52"] { height: 52%; }
[data-bottom-pct="52"] { bottom: 52%; }
[data-top-pct="52"] { top: 52%; }
[data-width-pct="52"] { width: 52%; }
[data-height-pct="53"] { height: 53%; }
[data-bottom-pct="53"] { bottom: 53%; }
[data-top-pct="53"] { top: 53%; }
[data-width-pct="53"] { width: 53%; }
[data-height-pct="54"] { height: 54%; }
[data-bottom-pct="54"] { bottom: 54%; }
[data-top-pct="54"] { top: 54%; }
[data-width-pct="54"] { width: 54%; }
[data-height-pct="55"] { height: 55%; }
[data-bottom-pct="55"] { bottom: 55%; }
[data-top-pct="55"] { top: 55%; }
[data-width-pct="55"] { width: 55%; }
[data-height-pct="56"] { height: 56%; }
[data-bottom-pct="56"] { bottom: 56%; }
[data-top-pct="56"] { top: 56%; }
[data-width-pct="56"] { width: 56%; }
[data-height-pct="57"] { height: 57%; }
[data-bottom-pct="57"] { bottom: 57%; }
[data-top-pct="57"] { top: 57%; }
[data-width-pct="57"] { width: 57%; }
[data-height-pct="58"] { height: 58%; }
[data-bottom-pct="58"] { bottom: 58%; }
[data-top-pct="58"] { top: 58%; }
[data-width-pct="58"] { width: 58%; }
[data-height-pct="59"] { height: 59%; }
[data-bottom-pct="59"] { bottom: 59%; }
[data-top-pct="59"] { top: 59%; }
[data-width-pct="59"] { width: 59%; }
[data-height-pct="60"] { height: 60%; }
[data-bottom-pct="60"] { bottom: 60%; }
[data-top-pct="60"] { top: 60%; }
[data-width-pct="60"] { width: 60%; }
[data-height-pct="61"] { height: 61%; }
[data-bottom-pct="61"] { bottom: 61%; }
[data-top-pct="61"] { top: 61%; }
[data-width-pct="61"] { width: 61%; }
[data-height-pct="62"] { height: 62%; }
[data-bottom-pct="62"] { bottom: 62%; }
[data-top-pct="62"] { top: 62%; }
[data-width-pct="62"] { width: 62%; }
[data-height-pct="63"] { height: 63%; }
[data-bottom-pct="63"] { bottom: 63%; }
[data-top-pct="63"] { top: 63%; }
[data-width-pct="63"] { width: 63%; }
[data-height-pct="64"] { height: 64%; }
[data-bottom-pct="64"] { bottom: 64%; }
[data-top-pct="64"] { top: 64%; }
[data-width-pct="64"] { width: 64%; }
[data-height-pct="65"] { height: 65%; }
[data-bottom-pct="65"] { bottom: 65%; }
[data-top-pct="65"] { top: 65%; }
[data-width-pct="65"] { width: 65%; }
[data-height-pct="66"] { height: 66%; }
[data-bottom-pct="66"] { bottom: 66%; }
[data-top-pct="66"] { top: 66%; }
[data-width-pct="66"] { width: 66%; }
[data-height-pct="67"] { height: 67%; }
[data-bottom-pct="67"] { bottom: 67%; }
[data-top-pct="67"] { top: 67%; }
[data-width-pct="67"] { width: 67%; }
[data-height-pct="68"] { height: 68%; }
[data-bottom-pct="68"] { bottom: 68%; }
[data-top-pct="68"] { top: 68%; }
[data-width-pct="68"] { width: 68%; }
[data-height-pct="69"] { height: 69%; }
[data-bottom-pct="69"] { bottom: 69%; }
[data-top-pct="69"] { top: 69%; }
[data-width-pct="69"] { width: 69%; }
[data-height-pct="70"] { height: 70%; }
[data-bottom-pct="70"] { bottom: 70%; }
[data-top-pct="70"] { top: 70%; }
[data-width-pct="70"] { width: 70%; }
[data-height-pct="71"] { height: 71%; }
[data-bottom-pct="71"] { bottom: 71%; }
[data-top-pct="71"] { top: 71%; }
[data-width-pct="71"] { width: 71%; }
[data-height-pct="72"] { height: 72%; }
[data-bottom-pct="72"] { bottom: 72%; }
[data-top-pct="72"] { top: 72%; }
[data-width-pct="72"] { width: 72%; }
[data-height-pct="73"] { height: 73%; }
[data-bottom-pct="73"] { bottom: 73%; }
[data-top-pct="73"] { top: 73%; }
[data-width-pct="73"] { width: 73%; }
[data-height-pct="74"] { height: 74%; }
[data-bottom-pct="74"] { bottom: 74%; }
[data-top-pct="74"] { top: 74%; }
[data-width-pct="74"] { width: 74%; }
[data-height-pct="75"] { height: 75%; }
[data-bottom-pct="75"] { bottom: 75%; }
[data-top-pct="75"] { top: 75%; }
[data-width-pct="75"] { width: 75%; }
[data-height-pct="76"] { height: 76%; }
[data-bottom-pct="76"] { bottom: 76%; }
[data-top-pct="76"] { top: 76%; }
[data-width-pct="76"] { width: 76%; }
[data-height-pct="77"] { height: 77%; }
[data-bottom-pct="77"] { bottom: 77%; }
[data-top-pct="77"] { top: 77%; }
[data-width-pct="77"] { width: 77%; }
[data-height-pct="78"] { height: 78%; }
[data-bottom-pct="78"] { bottom: 78%; }
[data-top-pct="78"] { top: 78%; }
[data-width-pct="78"] { width: 78%; }
[data-height-pct="79"] { height: 79%; }
[data-bottom-pct="79"] { bottom: 79%; }
[data-top-pct="79"] { top: 79%; }
[data-width-pct="79"] { width: 79%; }
[data-height-pct="80"] { height: 80%; }
[data-bottom-pct="80"] { bottom: 80%; }
[data-top-pct="80"] { top: 80%; }
[data-width-pct="80"] { width: 80%; }
[data-height-pct="81"] { height: 81%; }
[data-bottom-pct="81"] { bottom: 81%; }
[data-top-pct="81"] { top: 81%; }
[data-width-pct="81"] { width: 81%; }
[data-height-pct="82"] { height: 82%; }
[data-bottom-pct="82"] { bottom: 82%; }
[data-top-pct="82"] { top: 82%; }
[data-width-pct="82"] { width: 82%; }
[data-height-pct="83"] { height: 83%; }
[data-bottom-pct="83"] { bottom: 83%; }
[data-top-pct="83"] { top: 83%; }
[data-width-pct="83"] { width: 83%; }
[data-height-pct="84"] { height: 84%; }
[data-bottom-pct="84"] { bottom: 84%; }
[data-top-pct="84"] { top: 84%; }
[data-width-pct="84"] { width: 84%; }
[data-height-pct="85"] { height: 85%; }
[data-bottom-pct="85"] { bottom: 85%; }
[data-top-pct="85"] { top: 85%; }
[data-width-pct="85"] { width: 85%; }
[data-height-pct="86"] { height: 86%; }
[data-bottom-pct="86"] { bottom: 86%; }
[data-top-pct="86"] { top: 86%; }
[data-width-pct="86"] { width: 86%; }
[data-height-pct="87"] { height: 87%; }
[data-bottom-pct="87"] { bottom: 87%; }
[data-top-pct="87"] { top: 87%; }
[data-width-pct="87"] { width: 87%; }
[data-height-pct="88"] { height: 88%; }
[data-bottom-pct="88"] { bottom: 88%; }
[data-top-pct="88"] { top: 88%; }
[data-width-pct="88"] { width: 88%; }
[data-height-pct="89"] { height: 89%; }
[data-bottom-pct="89"] { bottom: 89%; }
[data-top-pct="89"] { top: 89%; }
[data-width-pct="89"] { width: 89%; }
[data-height-pct="90"] { height: 90%; }
[data-bottom-pct="90"] { bottom: 90%; }
[data-top-pct="90"] { top: 90%; }
[data-width-pct="90"] { width: 90%; }
[data-height-pct="91"] { height: 91%; }
[data-bottom-pct="91"] { bottom: 91%; }
[data-top-pct="91"] { top: 91%; }
[data-width-pct="91"] { width: 91%; }
[data-height-pct="92"] { height: 92%; }
[data-bottom-pct="92"] { bottom: 92%; }
[data-top-pct="92"] { top: 92%; }
[data-width-pct="92"] { width: 92%; }
[data-height-pct="93"] { height: 93%; }
[data-bottom-pct="93"] { bottom: 93%; }
[data-top-pct="93"] { top: 93%; }
[data-width-pct="93"] { width: 93%; }
[data-height-pct="94"] { height: 94%; }
[data-bottom-pct="94"] { bottom: 94%; }
[data-top-pct="94"] { top: 94%; }
[data-width-pct="94"] { width: 94%; }
[data-height-pct="95"] { height: 95%; }
[data-bottom-pct="95"] { bottom: 95%; }
[data-top-pct="95"] { top: 95%; }
[data-width-pct="95"] { width: 95%; }
[data-height-pct="96"] { height: 96%; }
[data-bottom-pct="96"] { bottom: 96%; }
[data-top-pct="96"] { top: 96%; }
[data-width-pct="96"] { width: 96%; }
[data-height-pct="97"] { height: 97%; }
[data-bottom-pct="97"] { bottom: 97%; }
[data-top-pct="97"] { top: 97%; }
[data-width-pct="97"] { width: 97%; }
[data-height-pct="98"] { height: 98%; }
[data-bottom-pct="98"] { bottom: 98%; }
[data-top-pct="98"] { top: 98%; }
[data-width-pct="98"] { width: 98%; }
[data-height-pct="99"] { height: 99%; }
[data-bottom-pct="99"] { bottom: 99%; }
[data-top-pct="99"] { top: 99%; }
[data-width-pct="99"] { width: 99%; }
[data-height-pct="100"] { height: 100%; }
[data-bottom-pct="100"] { bottom: 100%; }
[data-top-pct="100"] { top: 100%; }
[data-width-pct="100"] { width: 100%; }

  /* ── COMMERCIAL SPLIT + JOB SHEET TICKS (24 Jul 2026) ─ */
  .jobtype-row { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; margin-bottom: 18px; }
  .jobtype-label { font-size: 11px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--mid-grey); }
  .jobtype-btns { display: inline-flex; background: var(--cream); border: 1.5px solid var(--light-grey); border-radius: 10px; padding: 3px; gap: 3px; }
  .jobtype-btn { border: 0; background: transparent; font-family: var(--font-body); font-size: 12.5px; font-weight: 600; color: var(--mid-grey); padding: 7px 16px; border-radius: 7px; cursor: pointer; transition: all 0.15s; }
  .jobtype-btn.active { background: var(--sage); color: white; }
  #jobTypeCommercial.active { background: var(--gold); }
  #commercialFields { margin-bottom: 18px; }
  /* One command row under the title: filter chips lead, search stretches in
     the middle, New Job closes the line. Generous rhythm — compact reads cheap. */
  /* Equal 24px rhythm between title, command row and stats (matching
     .page-head); the chips stay deliberately tight to the board they filter. */
  .pipe-controls { display: flex; align-items: center; gap: 12px; margin-bottom: 24px; flex-wrap: wrap; }
  .pipe-controls .search-wrap { flex: 1; min-width: 220px; margin-bottom: 0; }
  .pipe-filters { display: flex; gap: 8px; flex-wrap: nowrap; margin-bottom: 14px; }
  .col-count { opacity: 0.7; }
  .pipeline-stats { margin-bottom: 24px; }
  /* Commercial cards look identical to residential except the postcode pill
     goes gold — the owner's chosen single, quiet marker. */
  .job-card.commercial .job-card-postcode { color: #97781f; background: rgba(163,50,54,0.16); }
  /* Made and Fitted were 17px checkboxes, less than half the 44px guideline,
     and they are the most-tapped controls in the app for a fitter on site.
     Half a row each, filled when done, readable across a room. */
  .blind-ticks { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; margin-top: 12px; }
  @media (max-width: 460px) { .blind-ticks { grid-template-columns: 1fr 1fr; } .hold-btn { grid-column: span 2; } }
  .tick-btn { display: flex; align-items: center; justify-content: center; gap: 9px; min-height: 46px; padding: 0 12px; border: 1.5px solid var(--light-grey); border-radius: 11px; background: var(--warm-white); font-size: 13.5px; font-weight: 650; color: var(--charcoal); cursor: pointer; transition: background 0.15s, border-color 0.15s, color 0.15s; }
  .tick-btn input { position: absolute; opacity: 0; width: 0; height: 0; }
  .tick-btn:has(input:checked) { background: var(--sage); border-color: var(--sage); color: #fff; }
  .tick-btn:has(input:focus-visible) { outline: 2px solid var(--sage); outline-offset: 2px; }
  /* On hold is not progress, it is a decision not to buy yet, so it wears the
     gold the app already uses for parked things rather than the sage of done. */
  .hold-btn:has(input:checked) { background: var(--sage); border-color: var(--sage); color: #fff; }
  /* A held blind stays in the list, plainly out of the quote. */
  .blind-item:has(.hold-btn input:checked) { border-style: dashed; }
  .blind-item:has(.hold-btn input:checked) .blind-price-tag { color: var(--mid-grey); text-decoration: line-through; }
  .doc-progress { white-space: nowrap; }
  .doc-tick { cursor: pointer; font-size: 17px; user-select: none; padding: 2px 3px; vertical-align: -1px; }
  .doc-tick.saving { cursor: wait; opacity: 0.55; }
  .mod-motor { align-self: end; }
  .type-tag { display: inline-block; margin-left: 8px; padding: 2px 10px; border-radius: 20px; font-size: 10.5px; font-weight: 700; letter-spacing: 0.04em; vertical-align: 2px; background: #ecebe8; color: var(--forest); }
  .type-tag.commercial { background: rgba(163,50,54,0.16); color: #97781f; }
  /* Premium spacing pass (24 Jul): same information, more air — cramped reads
     as cheap. Postcode pills never wrap; overflowing ones ellipsise instead. */
  .job-card { padding: 17px 12px; }
  .job-card-top { flex-wrap: nowrap; gap: 6px; margin-bottom: 10px; }
  .job-card-postcode { max-width: none; flex: none; overflow: visible; text-overflow: clip; padding-inline: 7px; letter-spacing: 0.4px; }
  .job-card-total { white-space: nowrap; flex: none; font-size: 14.5px; }
  .job-card-name { margin-bottom: 3px; }
  .job-card-meta { line-height: 1.65; }
  .stat-card { padding: 22px; }
  .empty-col { min-height: 56px; padding: 16px 8px; }

  /* Analytics filters: chips in their own row, the custom range beneath on
     mobile. iPhones render empty date inputs as blank pills, so an iOS-only
     overlay shows dd/mm/yyyy until a value exists (Android and desktop have
     native hints — the @supports gate stops doubling). */
  .ana-chips { display: flex; gap: 8px; align-items: center; }
  .flt-custom { display: flex; align-items: center; gap: 6px; }
  .date-wrap { position: relative; display: inline-flex; }
  .date-ph { display: none; position: absolute; left: 12px; top: 50%; transform: translateY(-50%); font-size: 13px; color: #b9b2a6; pointer-events: none; }
  @supports (-webkit-touch-callout: none) {
    .date-wrap.empty .date-ph { display: block; }
  }
  @media (max-width: 860px) {
    .ana-chips { overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch; scrollbar-width: none; padding-bottom: 4px; max-width: 100%; }
    .ana-chips::-webkit-scrollbar { display: none; }
    .ana-filters { flex-wrap: wrap; }
    /* Keep each short label beside its date field: From [date] To [date]. */
    .flt-custom { flex-basis: 100%; display: grid; grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr); gap: 6px; }
    .flt-date-label { font-size: 11px; font-weight: 700; letter-spacing: 0.6px; text-transform: uppercase; white-space: nowrap; }
    .flt-custom .date-wrap { min-width: 0; }
    .flt-custom input[type="date"] { width: 100%; min-width: 0; min-height: 42px; padding-inline: 7px; }
  }

  /* ── MOBILE DRAG & DROP (24 Jul 2026) ─────────────────── */
  /* Cards behave like app objects on touch: no text-selection loupe, no
     double-tap zoom, normal scrolling untouched. */
  .job-card { -webkit-touch-callout: none; user-select: none; -webkit-user-select: none; touch-action: manipulation; }
  .mdrag-ghost { position: fixed; left: 0; top: 0; width: 210px; z-index: 900; pointer-events: none; opacity: 0.94; box-shadow: 0 14px 34px rgba(31,33,36,0.35); transform: translate(-9999px, -9999px); }
  .job-card.touch-lift { opacity: 0.35; border-style: dashed; }
  body.mdragging { user-select: none; -webkit-user-select: none; }

  /* Card menu: archive and trash, at the end of the dates row. */
  .job-card-foot { display: flex; align-items: flex-end; gap: 8px; }
  .job-card-dates { flex: 1; min-width: 0; }
  .job-card-menu { flex: none; border: none; background: transparent; padding: 2px 0 0; margin: 0; cursor: pointer; color: var(--light-grey); line-height: 0; }
  .job-card-menu:hover { color: var(--forest); }
  .job-card-menu .ic { width: 17px; height: 17px; }
  .job-card { position: relative; }
  .card-menu { position: absolute; right: 10px; bottom: 34px; z-index: 20; background: var(--warm-white); border: 1px solid var(--light-grey); border-radius: 10px; padding: 4px; box-shadow: var(--shadow-lg); min-width: 168px; }
  .card-menu-item { display: flex; align-items: center; gap: 9px; width: 100%; border: none; background: transparent; padding: 9px 11px; border-radius: 7px; font-family: var(--font-body); font-size: 13px; font-weight: 500; color: var(--charcoal); cursor: pointer; text-align: left; }
  .card-menu-item:hover { background: var(--cream); }
  .card-menu-item.danger { color: var(--red); }
  .card-menu-item .ic { width: 15px; height: 15px; }
  .trash-pill.owing { color: var(--red); border-color: var(--red); }

  /* Branded questions, in place of native confirm/prompt. The native ones
     announce the domain, cannot be styled and read as browser errors rather
     than the app asking something. */
  .ask-overlay { position: fixed; inset: 0; z-index: 400; background: rgba(31,33,36,0.42); display: flex; align-items: center; justify-content: center; padding: 22px; }
  body.ask-shown { overflow: hidden; }
  .ask-card { background: var(--warm-white); border-radius: 16px; padding: 24px 24px 18px; width: 100%; max-width: 420px; box-shadow: var(--shadow-lg); border-top: 4px solid var(--sage); }
  .ask-card.danger { border-top-color: var(--red); }
  .ask-title { font-family: var(--font-display); font-size: 18px; font-weight: 600; color: var(--forest); line-height: 1.35; margin-bottom: 10px; }
  .ask-text { font-size: 13.5px; line-height: 1.6; color: var(--mid-grey); margin: 0 0 8px; }
  /* 16px, not 14: iOS zooms the whole page the moment it focuses a field
     smaller than that, and askText() focuses this one as it opens. The mobile
     rule that fixes every other input is `input, select, textarea`, which a
     class selector outranks, which is how this one alone kept the bug. Anything
     added here that a finger types into wants 16px for the same reason. */
  .ask-input { width: 100%; margin: 12px 0 4px; padding: 11px 13px; border: 1.5px solid var(--light-grey); border-radius: 10px; font-family: var(--font-body); font-size: 16px; color: var(--charcoal); background: white; }
  .ask-input:focus { outline: none; border-color: var(--sage); box-shadow: 0 0 0 3px rgba(74,79,86,0.15); }
  /* Two equal columns filling the card: one decision, two answers of equal
     weight, neither of them a small target off in the corner. */
  .ask-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 18px; }
  .ask-btn { padding: 10px 17px; border-radius: 10px; border: 1.5px solid var(--light-grey); background: transparent; font-family: var(--font-body); font-size: 13.5px; font-weight: 600; color: var(--mid-grey); cursor: pointer; }
  .ask-btn:hover { border-color: var(--sage); color: var(--forest); }
  .ask-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(74,79,86,0.25); }
  .ask-overlay[data-opening] .ask-btn:focus-visible { box-shadow: none; }
  .ask-btn.danger { border-color: rgba(168,68,56,0.45); color: var(--red); }
  .ask-btn.danger:hover { background: #fde8e8; border-color: var(--red); color: var(--red); }
  .ask-btn.danger:focus-visible { box-shadow: 0 0 0 3px rgba(168,68,56,0.22); }
  .ask-btn.primary { background: var(--forest); border-color: var(--forest); color: var(--cream); }
  .ask-btn.primary:hover { background: var(--sage); border-color: var(--sage); color: white; }
  .ask-btn.primary.danger { background: var(--red); border-color: var(--red); color: white; }
  .ask-btn.primary.danger:hover { opacity: 0.9; }
  /* On a phone the buttons go full width and the confirm sits on top, where
     the thumb already is. */
  @media (max-width: 560px) {
    .ask-card { padding: 20px 18px 16px; }
    .ask-actions { display: flex; flex-direction: column-reverse; }
    .ask-btn { width: 100%; padding: 13px 17px; }
  }

  /* Trash and Archive: search, count and pager. */
  .list-controls { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
  .list-controls .search-wrap { flex: 1; min-width: 200px; margin-bottom: 0; }
  .list-count { font-size: 12px; color: var(--mid-grey); white-space: nowrap; }
  .list-pager { display: flex; align-items: center; justify-content: center; gap: 14px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--light-grey); }
  .list-pager.is-hidden { display: none; }
  .pager-label { font-size: 12.5px; color: var(--mid-grey); }

/* The gate's own explanation when the browser threw the last sign-in away
   (Private Browsing, cleared website data). Quiet, informational, and only
   shown when storage arrived empty outside the installed app. */
.gate-storage-hint { margin-top: 12px; font-size: 0.82rem; line-height: 1.5; color: var(--sage); }

/* Independent setup screen; inherited UI styling is adapted in the next stage. */
.setup-page { min-height:100dvh; display:grid; place-items:center; padding:28px; background:#f6f5f2; color:#202226; }
.setup-card { width:100%; max-width:580px; padding:48px; background:#fff; border:1px solid #e6e4df; border-radius:18px; box-shadow:0 12px 40px rgb(0 0 0 / 4%); }
.setup-label { color:#a33236; font-size:10px; letter-spacing:1.8px; font-weight:700; margin-bottom:18px; }
.setup-card h1 { font-size:40px; letter-spacing:-1.4px; margin-bottom:20px; }
.setup-card p:not(.setup-label) { line-height:1.7; color:#686a6d; font-size:15px; }
.setup-card .setup-status { margin-top:30px; padding-top:20px; border-top:1px solid #eceae6; font-size:12px; }
@media(max-width:480px) { .setup-card { padding:30px 24px; } .setup-card h1 { font-size:34px; } }

/* ── DRIVEHUB UK GARAGE ADDITIONS ───────────────────────────────────
   The layout above is the original job system's. Everything below adapts it
   to the garage: white text on the red primary buttons, garage stage colours,
   registration plates where postcodes were, the sign-in gate, the diary and
   the work-line grid. */
.side-new, .mob-new, .rail-save, .dock-save, .btn-save, .csp-utility-43, .stage-save { color: #fff; }
.side-new:hover, .mob-new:hover, .rail-save:hover, .dock-save:hover, .btn-save:hover, .csp-utility-43:hover { background: var(--gold-deep); color: #fff; }
.status-current.deposit { background: var(--gold); color: #fff; }
.pipeline-board { grid-template-columns: repeat(8, minmax(268px, 1fr)); }
.col-header.draft { background: #ebe9e4; color: #5f6368; }
.col-header.booked { background: #e7edf5; color: #44669a; }
.col-header.vehicle_in { background: #e0ecf1; color: #2e5e73; }
.col-header.awaiting_approval { background: #f5edd6; color: #97781f; }
.col-header.awaiting_parts { background: #f7e8e0; color: #b05e3d; }
.col-header.in_progress { background: #ece5f4; color: #71519b; }
.col-header.ready { background: #e4efe7; color: #3b7a4f; }
.col-header.collected { background: #e6e4e0; color: #1f2124; }
.col-header.cancelled { background: #f6e3e1; color: #a84438; }
.status-current.draft { background: #5f6368; color: #fff; }
.status-current.booked { background: #44669a; color: #fff; }
.status-current.vehicle_in { background: #2e5e73; color: #fff; }
.status-current.awaiting_approval { background: #97781f; color: #fff; }
.status-current.awaiting_parts { background: #b05e3d; color: #fff; }
.status-current.in_progress { background: #71519b; color: #fff; }
.status-current.ready { background: #3b7a4f; color: #fff; }
.status-current.collected { background: #1f2124; color: #fff; }
.status-current.cancelled { background: #a84438; color: #fff; }
.cj-status.draft { background: #ebe9e4; color: #5f6368; }
.cj-status.booked { background: #e7edf5; color: #44669a; }
.cj-status.vehicle_in { background: #e0ecf1; color: #2e5e73; }
.cj-status.awaiting_approval { background: #f5edd6; color: #97781f; }
.cj-status.awaiting_parts { background: #f7e8e0; color: #b05e3d; }
.cj-status.in_progress { background: #ece5f4; color: #71519b; }
.cj-status.ready { background: #e4efe7; color: #3b7a4f; }
.cj-status.collected { background: #e6e4e0; color: #1f2124; }
.cj-status.cancelled { background: #f6e3e1; color: #a84438; }
.cj-status.confirmed { background: #e7edf5; color: #44669a; }
.cj-status.arrived { background: #e4efe7; color: #3b7a4f; }
.cj-status.completed { background: #e6e4e0; color: #1f2124; }
.cj-status.no_show { background: #f6e3e1; color: #a84438; }
.cj-status.kind { background: #fff; border: 1px solid var(--light-grey); color: var(--mid-grey); }
.stage-dot.draft { background: #5f6368; } .stage-dot.booked { background: #44669a; } .stage-dot.vehicle_in { background: #2e5e73; }
.stage-dot.awaiting_approval { background: #97781f; } .stage-dot.awaiting_parts { background: #b05e3d; } .stage-dot.in_progress { background: #71519b; }
.stage-dot.ready { background: #3b7a4f; } .stage-dot.collected { background: #1f2124; } .stage-dot.cancelled { background: #a84438; }
.job-card-postcode, .cust-pc, .plate { font-family: var(--font-display); letter-spacing: 1px; border: 1.5px solid var(--forest); background: #fff; color: var(--forest); }
.plate { display: inline-block; font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 6px; white-space: nowrap; }
.job-card-address { white-space: normal; }
.sec-sub { font-size: 12.5px; color: var(--mid-grey); margin: -10px 0 14px; }
/* Sign-in gate */
.gate { position: fixed; inset: 0; z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 20px; background: var(--forest); }
.gate-card { background: var(--warm-white); border-radius: 16px; padding: 36px 32px; max-width: 380px; width: 100%; text-align: center; box-shadow: 0 20px 60px rgba(0,0,0,0.4); }
.gate-label { color: var(--gold); font-size: 10px; letter-spacing: 1.8px; font-weight: 700; margin-bottom: 14px; text-transform: uppercase; }
.gate-card h1 { font-family: var(--font-display); font-size: 26px; font-weight: 700; color: var(--forest); margin-bottom: 4px; }
.gate-card p { color: var(--charcoal); opacity: 0.7; font-size: 14px; margin-bottom: 22px; }
.gate-card input { width: 100%; padding: 14px 16px; border: 2px solid var(--light-grey); border-radius: 12px; font-size: 16px; margin-bottom: 10px; background: #fff; }
.gate-btn { width: 100%; padding: 14px 16px; background: var(--gold); color: #fff; border: none; border-radius: 12px; font-size: 15px; font-weight: 700; cursor: pointer; font-family: var(--font-body); }
.gate-link { background: none; border: none; color: var(--sage); font-family: var(--font-body); font-size: 13px; font-weight: 600; cursor: pointer; margin-top: 14px; text-decoration: underline; }
.gate-error { color: #b3403f; font-size: 13px; margin-top: 10px; min-height: 16px; }
.gate-error.ok { color: #3b7a4f; }
.gate-status { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--light-grey); font-size: 12px; color: var(--mid-grey); }
.loading-note { padding: 40px; text-align: center; color: var(--mid-grey); }
/* Diary */
.diary-day { font-family: var(--font-display); font-size: 13px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--mid-grey); margin: 6px 0 10px; }
.diary-time { font-family: var(--font-display); font-weight: 700; color: var(--forest); min-width: 48px; font-variant-numeric: tabular-nums; }
.contact-job.diary-row { padding: 12px 6px; }
.contact-job .cj-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.contact-job .cj-sub { font-size: 12px; color: var(--mid-grey); width: 100%; }
/* Vehicles inside a contact card */
.veh-row { display: flex; align-items: center; gap: 10px; padding: 9px 4px; border-bottom: 1px solid var(--light-grey); font-size: 13px; flex-wrap: wrap; }
.veh-row:last-child { border-bottom: none; }
.veh-row .grow { flex: 1; min-width: 0; }
.veh-row .veh-meta { color: var(--mid-grey); font-size: 12px; }
.contact-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
/* Work lines grid on the job form */
.blind-fields.line-grid { grid-template-columns: minmax(0, 2.2fr) minmax(0, 1fr) minmax(0, 0.7fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.1fr); }
@media (max-width: 860px) { .blind-fields.line-grid { grid-template-columns: 1fr 1fr; } .blind-fields.line-grid .field.wide { grid-column: span 2; } }
.line-done { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 600; color: var(--mid-grey); margin-top: 10px; }
.line-done input { width: 18px; height: 18px; accent-color: var(--sage); }
.blind-item.done .blind-price-tag { color: #3b7a4f; }
.add-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 560px) { .add-row { grid-template-columns: 1fr; } }
/* Booking editor extras */
.bk-ed-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
.bk-ed-grid label { display: block; font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--mid-grey); margin-bottom: 5px; }
/* Dialog forms */
.ask-card.wide { max-width: 560px; }
.ask-card .form-grid { margin-top: 6px; }
.ask-card .form-grid + .form-grid { margin-top: 14px; }
.ask-card label { text-align: left; }
.ask-btn.primary { background: var(--gold); border-color: var(--gold); color: #fff; }
.ask-btn.primary.danger { background: var(--red); border-color: var(--red); }
.ask-btn.danger { color: var(--red); border-color: var(--red); }
/* Price list rows */
.price-row { display: flex; align-items: center; gap: 12px; padding: 11px 2px; border-bottom: 1px solid var(--light-grey); cursor: pointer; }
.price-row:last-child { border-bottom: none; }
.price-row:hover { background: var(--cream); }
.price-row .grow { flex: 1; min-width: 0; }
.price-name { font-weight: 600; color: var(--forest); font-size: 14px; }
.price-meta { font-size: 12px; color: var(--mid-grey); margin-top: 2px; }
.price-amount { font-family: var(--font-display); font-weight: 700; color: var(--forest); white-space: nowrap; }
.price-off { opacity: 0.5; }
.cust-lines .cust-txt small { display: block; }
.empty-note { font-size: 13px; color: var(--mid-grey); padding: 6px 0; }
.match-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }

/* The hidden attribute must win over component display rules. */
[hidden] { display: none !important; }

/* ── Stage 4: documents, payments and approvals ───────────────── */
.job-card-pay { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; padding: 3px 8px; border-radius: 6px; flex: none; }
.job-card-pay.paid { background: #e4efe7; color: #3b7a4f; }
.job-card-pay.part_paid { background: #f5edd6; color: #97781f; }
.job-card-pay.unpaid { background: #f6e3e1; color: #a84438; }
.job-card-pay.credit { background: #e0ecf1; color: #2e5e73; }
.totals-row.quiet { color: var(--mint); font-size: 13px; padding: 5px 0; }
.totals-row.quiet .label { color: var(--mint); }
.totals-row.quiet span:last-child { text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-left: 12px; }
.pay-void-note { margin-top: 6px; white-space: normal; }
.pay-form .deposit-field.field { margin: 0; }
.pay-refund-check { display: flex; align-items: center; gap: 10px; color: rgba(255,255,255,0.75); font-size: 13px; cursor: pointer; }
.pay-refund-check input { width: 18px; height: 18px; accent-color: var(--pay-gold); }
.doc-list { display: flex; flex-direction: column; margin-top: 6px; }
.doc-list-row { display: flex; align-items: center; gap: 12px; width: 100%; padding: 12px 6px; border: none; border-bottom: 1px solid var(--light-grey); background: none; text-align: left; cursor: pointer; font-family: var(--font-body); }
.doc-list-row:hover { background: var(--cream); }
.doc-list-kind { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; padding: 3px 8px; border-radius: 6px; background: var(--cream); color: var(--sage); flex: none; }
.doc-list-kind.quote { background: #e7edf5; color: #44669a; }
.doc-list-kind.invoice { background: #e0ecf1; color: #2e5e73; }
.doc-list-kind.receipt { background: #e4efe7; color: #3b7a4f; }
.doc-list-kind.credit_note { background: #f6e3e1; color: #a84438; }
.doc-list-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.doc-list-num { font-weight: 700; color: var(--forest); font-size: 14px; }
.doc-list-row.is-old .doc-list-num { text-decoration: line-through; color: var(--mid-grey); }
.doc-list-meta { font-size: 12px; color: var(--mid-grey); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.doc-list-amount { font-family: var(--font-display); font-weight: 700; color: var(--forest); font-variant-numeric: tabular-nums; flex: none; }
.doc-customer h3.doc-sub { margin-top: 14px; }
.doc-table th.num, .doc-table td.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.doc-table td.qty { white-space: nowrap; }
.doc-stamp { display: inline-block; margin-top: 8px; padding: 4px 16px; border: 2.5px solid var(--red); border-radius: 8px; color: var(--red); font-size: 14px; font-weight: 800; letter-spacing: 2px; }
.doc-note { margin-bottom: 20px; padding: 12px 16px; background: var(--cream); border-radius: 10px; font-size: 13px; color: var(--charcoal); line-height: 1.5; white-space: pre-line; }
.doc-terms p + p { margin-top: 8px; }
.pre { white-space: pre-line; }
.doc-actions .btn-secondary[disabled] { opacity: 0.45; cursor: not-allowed; }
.doc-actions .btn-secondary[disabled]:hover { background: transparent; color: var(--sage); }
.doc-actions .btn-danger { display: inline-flex; align-items: center; gap: 8px; }
.doc-actions .btn-save { flex: none; }
#printArea .doc-table th.num, #printArea .doc-table td.num { text-align: right; }
#printArea .doc-stamp { display: inline-block; margin-top: 8px; padding: 4px 16px; border: 2.5px solid #a84438; border-radius: 8px; color: #a84438; font-size: 14px; font-weight: 800; letter-spacing: 2px; }
#printArea .doc-paid { display: inline-block; margin-top: 8px; padding: 4px 16px; border: 2.5px solid #4a4f56; border-radius: 8px; color: #4a4f56; font-size: 14px; font-weight: 800; letter-spacing: 2px; }
#printArea .doc-note { margin-bottom: 20px; padding: 12px 16px; background: #f6f4f0; border-radius: 10px; font-size: 13px; line-height: 1.5; white-space: pre-line; }
#printArea .doc-customer h3.doc-sub { margin-top: 14px; }
#printArea .pre { white-space: pre-line; }
