/* Light-only overrides — detail cluster.
   ─────────────────────────────────────────────────────────────────────────────────────────────
   WHY THIS FILE EXISTS, AND WHY IT IS NOT IN A PAGE STYLESHEET

   A light-only rule cannot live in a page sheet. usePageStyle confines those by CSS nesting when a
   page renders inside a pane, which compiles `html[data-theme="light"] .foo` into a DESCENDANT of
   the page scope — and the root element has no ancestors, so it can never match. Silently. One
   instance of that already shipped.

   It also cannot live in app.css while six agents are working, so the cluster gets its own file.

   HOW IT WINS
   These load after app.css but BEFORE the runtime-injected page sheets, so document order is not on
   their side. They win on SPECIFICITY: `html[data-theme="light"] .foo` is (0,2,0) against a page's
   (0,1,0). Against a two-class page selector, add one more class or an element to stay ahead.

   WHAT BELONGS HERE
   Only rules that must differ in light and whose dark value must not move. Every rule is prefixed
   `html[data-theme="light"]`, so dark cannot match by construction — which is what makes this file
   safe to write in parallel with the dark baseline frozen.

   WHAT DOES NOT BELONG HERE
   Anything that should be true in both themes; that goes in the page sheet. Anything needing a new
   token; that is escalated to the lead, who owns app.css.

   ─────────────────────────────────────────────────────────────────────────────────────────────
   SCOPING RULE FOR THIS FILE, AND IT IS NOT OPTIONAL

   Page sheets are injected globally (usePageStyle only pane-scopes inside the tablet shell), so the
   class names in this cluster are NOT unique: `.badge`, `.chat-pill`, `.info-label`, `.quote-card`,
   `.full-quote-btn`, `.feedback-msg` are each declared by a dozen pages with a dozen different
   values. A bare `html[data-theme="light"] .badge` here would repaint every page in the app and
   collide head-on with the five agents working the other clusters.

   Therefore every rule below is anchored on one of this cluster's five page roots:
       .detail-wrapper   /job-detail + /finished-job-detail   (used by no other page)
       .qdd-page         /quote-draft-detail
       .qhd-wrapper      /quote-history-detail
       .deck-wrapper     /quote-drafts

   ─────────────────────────────────────────────────────────────────────────────────────────────
   THE TWO JOBS THIS FILE DOES

   1. ACCENT DISCIPLINE. Violet earns the primary action, the focus ring, the selection and the
      logo. Everywhere else in light it is noise, and at alpha it is worse than noise: the whole
      Översikt label column on /quote-history-detail was `rgba(var(--ch-indigo-c4b5fd), 0.58)`,
      which in light resolves to 139,92,246 at .58 over white and measures 2.15:1 at 10.6px.
      `--text-muted` is 5.01:1 on the same ground. Every label ink, eyebrow, count, tick, tinted
      chip and tinted panel below goes neutral; links and primary actions keep the accent, and they
      keep the RE-DERIVED one (#6c3cb8, ~30% less chroma) rather than the pigment violets the page
      sheets compose locally (--c-indigo-b9a3ff resolves to #5d00c3 in light, --astra-accent-soft
      to #7c3aed).

   2. THE SLABS. `rgba(var(--ref-shadow-base), α)` used as a SURFACE is invisible in dark (the
      channel is 0,0,0 over a near-black ground) and a mid-grey slab in light (23,20,40 at full
      weight over white). Those sites are fixed in the page sheets with --well-*, because the dark
      value is byte-identical; only the one α with no token lands here. */


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   /quote-draft-detail — .qdd-page

   This is the page a customer's price arrives on, so it gets the most attention. Measured before:
   .qdd-msg (the letter the customer receives) #afaeb8, .qdd-totals #bcbac4 with the payable total
   at 2.87:1 on it, .qdd-foot with "1 sak kvar" at 1.86:1. Those are fixed in the page sheet
   (--well-a28 / -a22 / -a18 / -a14, all dark-identical). One is not.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* THE ONE THAT NEEDED THIS FILE. The foot is α .24, and the token ladder runs
   …/-a18/-a20/-a22/-a28 — there is no --well-a24. The α is not changed to fit a token (that would
   move dark); dark keeps its rgba(0,0,0,0.24) from the page sheet and light takes the nearest rung
   below, which is 2 lightness points off the interpolated value and imperceptible beside the
   totals block it never touches. ESCALATED: --well-a24 should exist. */
html[data-theme="light"] .qdd-page .qdd-foot { background: var(--well-a22); }

/* "1 sak kvar: svårighetsgrad" — the sentence stating what still blocks sending, sitting on that
   foot. It was painted with the literal --c-amber-fcd34d, which light resolves to #b87c05: 1.86:1
   on the old slab and still only 2.45:1 on the corrected one. It is a STATUS, so it takes the
   status token (rulebook §7, "never a literal") — --st-expected is #8a5500 in light and clears AA
   on the same ground. Same for the missing-price flag and the flagged booking pill. */
html[data-theme="light"] .qdd-page .qdd-blockers,
html[data-theme="light"] .qdd-page .qdd-flag,
html[data-theme="light"] .qdd-page .qdd-booked.flag,
html[data-theme="light"] .qdd-page .qdd-booked.flag b { color: var(--st-expected); }
html[data-theme="light"] .qdd-page .qdd-blockers:focus-visible { outline-color: var(--st-expected); }

/* The eyebrows. "OFFERTUTKAST", the three zone names, every ledger group name — all decorative
   violet, all restating a heading the reader already has. Neutral. */
html[data-theme="light"] .qdd-page .qdd-title,
html[data-theme="light"] .qdd-page .qdd-zone-head,
html[data-theme="light"] .qdd-page .qdd-group-name,
html[data-theme="light"] .qdd-page .qdd-pay-label { color: var(--text-muted); }

/* The letter's edge. A 2px violet rule down the one piece of prose on the page was the loudest
   object in the left zone; the rule is doing structure, not meaning. */
html[data-theme="light"] .qdd-page .qdd-msg { border-left-color: var(--border-strong); }

/* Text buttons and the pen ARE actions — the only pressable things in a column of prose — so they
   keep the accent. They just stop keeping the pigment one. */
html[data-theme="light"] .qdd-page .qdd-textbtn,
html[data-theme="light"] .qdd-page .qdd-zone .qdd-textbtn,
html[data-theme="light"] .qdd-page .qdd-field-edit,
html[data-theme="light"] .qdd-page .qdd-back:hover,
html[data-theme="light"] .qdd-page .qdd-line.reading:hover .qdd-pen,
html[data-theme="light"] .qdd-page .qdd-line.reading:focus-visible .qdd-pen { color: var(--accent-solid); }

/* "+ Lägg till rad" is a secondary action: surface and hairline, not a violet dashed outline. */
html[data-theme="light"] .qdd-page .qdd-add {
    border: 1px dashed var(--border-strong);
    color: var(--text);
}
html[data-theme="light"] .qdd-page .qdd-add:hover { border-style: solid; background: var(--surface-3-hover); }

/* A ledger row's hover. rgba(var(--ch-indigo-a78bfa), 0.07) is a violet wash in light; a row hover
   is a state, and states go darker (rulebook §3). */
html[data-theme="light"] .qdd-page .qdd-line.reading:hover,
html[data-theme="light"] .qdd-page .qdd-line.reading:focus-visible { background: var(--surface-hover); }

/* Focus is the one place violet is not only allowed but required. --focus-ring, not --fin-accent. */
html[data-theme="light"] .qdd-page .qdd-back:focus-visible,
html[data-theme="light"] .qdd-page .qdd-textbtn:focus-visible,
html[data-theme="light"] .qdd-page .qdd-add:focus-visible { outline-color: var(--focus-ring); }

/* Scrollbar furniture. */
html[data-theme="light"] .qdd-page .qdd-body { scrollbar-color: var(--border-strong) transparent; }
html[data-theme="light"] .qdd-page .qdd-body::-webkit-scrollbar-thumb { background: var(--border-strong); }


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   /quote-history-detail — .qhd-wrapper

   The measured worst case in the cluster: the Översikt panel's entire label column at 2.15:1.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* Label ink. This is violet-at-alpha doing a TEXT job, at 10.6px, on the panel that carries who the
   customer is, which car it is and what the quote came to. 2.15:1 → --text-muted's 5.01:1. */
html[data-theme="light"] .qhd-wrapper .detail-label,
html[data-theme="light"] .qhd-wrapper .wait-fact-label,
html[data-theme="light"] .qhd-wrapper .contact-call-label,
html[data-theme="light"] .qhd-wrapper .sec-cta { color: var(--text-muted); }
html[data-theme="light"] .qhd-wrapper .sec-toggle:hover .sec-cta { color: var(--text); }

/* The tick before each label. A glowing violet dot per row, six rows to a panel — decoration that
   reads as status. It stays as an anchor, in neutral, without the halo. */
html[data-theme="light"] .qhd-wrapper .detail-label::before {
    background: var(--border-strong);
    box-shadow: none;
}

/* Chevrons and glyphs are furniture. */
html[data-theme="light"] .qhd-wrapper .sec-chev,
html[data-theme="light"] .qhd-wrapper .total-toggle-chev,
html[data-theme="light"] .qhd-wrapper .wait-fact-ic,
html[data-theme="light"] .qhd-wrapper .wait-disclaimer svg { color: var(--text-muted); }

/* The total tile. A violet slab is the single largest block of accent on the page and it is not an
   action — it is a disclosure carrying a figure. Card surface, hairline, ink. */
html[data-theme="light"] .qhd-wrapper .total-toggle {
    background: var(--surface-3);
    border-color: var(--border-default);
}
html[data-theme="light"] .qhd-wrapper .total-toggle:hover {
    background: var(--surface-3-hover);
    border-color: var(--border-strong);
}
html[data-theme="light"] .qhd-wrapper .sec-toggle:hover { border-color: var(--border-strong); }
html[data-theme="light"] .qhd-wrapper .qb-row.grand .num { color: var(--text); }

/* The eyebrow badge and the origin chip: two more violet lozenges restating the page's own title. */
html[data-theme="light"] .qhd-wrapper .badge,
html[data-theme="light"] .qhd-wrapper .qh-source.src-agent {
    color: var(--text-muted);
    background: var(--surface-3);
    border-color: var(--border-default);
    box-shadow: none;
}

/* Chat trigger and equipment chips. */
html[data-theme="light"] .qhd-wrapper .chat-pill,
html[data-theme="light"] .qhd-wrapper .qhd-eq-chip,
html[data-theme="light"] .qhd-wrapper .qhd-phone-add {
    color: var(--text);
    background: var(--surface-3);
    border-color: var(--border-default);
    box-shadow: none;
}
html[data-theme="light"] .qhd-wrapper .chat-pill:hover,
html[data-theme="light"] .qhd-wrapper .qhd-phone-add:hover { background: var(--surface-3-hover); }

/* The call row: a link, so its verb keeps the accent; its tinted icon plate and violet hover do not. */
html[data-theme="light"] .qhd-wrapper .contact-call:hover {
    background: var(--surface-hover);
    border-color: var(--border-strong);
}
html[data-theme="light"] .qhd-wrapper .contact-call-ic {
    background: var(--surface-3);
    border: 1px solid var(--border-default);
    color: var(--text-muted);
}
html[data-theme="light"] .qhd-wrapper .contact-call-cta,
html[data-theme="light"] .qhd-wrapper .qhd-replaced-link,
html[data-theme="light"] .qhd-wrapper .back-btn { color: var(--accent-solid); }
html[data-theme="light"] .qhd-wrapper .back-btn:hover,
html[data-theme="light"] .qhd-wrapper .qhd-replaced-link:hover { color: var(--accent-solid-hover); }

/* Focus. */
/* `.date-input` deleted from this group 2026-08-11: dead sheet in
   `QuoteHistoryDetailDesktop.css`, emitted by no template under `src/`. */
html[data-theme="light"] .qhd-wrapper .qhd-phone-input:focus { border-color: var(--focus-ring); }

html[data-theme="light"] .qhd-wrapper .qhd-scroll { scrollbar-color: var(--border-strong) transparent; }
html[data-theme="light"] .qhd-wrapper .qhd-scroll::-webkit-scrollbar-thumb { background: var(--border-strong); }


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   /job-detail and /finished-job-detail — .detail-wrapper

   Both pages render the same root class and no other page in the app does, so one block serves
   both. Same defect as the quote history: a violet-at-alpha label column, violet ticks, and a
   repair-order panel that is the only tinted block on a white card.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

html[data-theme="light"] .detail-wrapper .info-label { color: var(--text-muted); }
html[data-theme="light"] .detail-wrapper .info-label::before {
    background: var(--border-strong);
    box-shadow: none;
}

/* The eyebrow badge on /job-detail ("MANUELL BOKNING"). The finished page's is emerald and means
   something — this one only repeats the page. */
html[data-theme="light"] .detail-wrapper .badge {
    color: var(--text-muted);
    background: var(--surface-3);
    border-color: var(--border-default);
    box-shadow: none;
}

/* Chat trigger, quote count, equipment chip: three violet lozenges, none of them an action's
   emphasis. */
html[data-theme="light"] .detail-wrapper .chat-pill,
html[data-theme="light"] .detail-wrapper .quote-count,
html[data-theme="light"] .detail-wrapper .eq-chip {
    color: var(--text);
    background: var(--surface-3);
    border-color: var(--border-default);
    box-shadow: none;
}
html[data-theme="light"] .detail-wrapper .chat-pill:hover { background: var(--surface-3-hover); }

/* The status band's action ("Hittat mer arbete", "Checka in"). A SECONDARY button sitting inside a
   band that is already tinted by the state it reports: surface, hairline, ink. The band keeps its
   own status tint — that is §7 vocabulary, not decoration. */
html[data-theme="light"] .detail-wrapper .sb-action {
    background: var(--surface-3);
    border-color: var(--border-default);
    color: var(--text);
}
html[data-theme="light"] .detail-wrapper .sb-action:hover {
    background: var(--surface-3-hover);
    border-color: var(--border-strong);
}
html[data-theme="light"] .detail-wrapper .sb-action:focus-visible { outline-color: var(--focus-ring); }

/* The name of the work, and the found-work continuation. The card's own heading value — it is not
   a link and it is not pressable, so it takes ink, at the size it already has. */
html[data-theme="light"] .detail-wrapper .info-value.work-name { color: var(--text); }
html[data-theme="light"] .detail-wrapper .work-added { color: var(--text-muted); }

/* Links stay accent, on the derived violet. */
html[data-theme="light"] .detail-wrapper .info-value.link-value,
html[data-theme="light"] .detail-wrapper .eq-field-action,
html[data-theme="light"] .detail-wrapper .hq-pen { color: var(--accent-solid); }
html[data-theme="light"] .detail-wrapper .info-value.link-value:hover,
html[data-theme="light"] .detail-wrapper .hq-pen:hover,
html[data-theme="light"] .detail-wrapper .eq-field-row:hover:not(:disabled) .eq-field-action { color: var(--accent-solid-hover); }
html[data-theme="light"] .detail-wrapper .ident-key.link-value:hover { color: var(--accent-solid); }

/* A quote line's amount is money, not a link. */
html[data-theme="light"] .detail-wrapper .hq-amount { color: var(--text); }

/* Secondary pill controls: "Visa hela offerten", the supersede fold, "+N fler", the rename save.
   All of them were a violet fill plus a violet border plus violet text. */
html[data-theme="light"] .detail-wrapper .full-quote-btn,
html[data-theme="light"] .detail-wrapper .supersede-toggle,
html[data-theme="light"] .detail-wrapper .quote-more-btn,
html[data-theme="light"] .detail-wrapper .hq-rename-btn.save,
html[data-theme="light"] .detail-wrapper .routine-open-btn {
    background: var(--surface-3);
    border: 1px solid var(--border-default);
    color: var(--text);
}
html[data-theme="light"] .detail-wrapper .full-quote-btn:hover,
html[data-theme="light"] .detail-wrapper .supersede-toggle:hover,
html[data-theme="light"] .detail-wrapper .quote-more-btn:hover,
html[data-theme="light"] .detail-wrapper .hq-rename-btn.save:hover,
html[data-theme="light"] .detail-wrapper .routine-open-btn:hover {
    background: var(--surface-3-hover);
    border-color: var(--border-strong);
    color: var(--text);
}

/* The routine slip. A tinted panel with a violet spine, saying "this visit's money lives on another
   board" — a statement, not an accent. The spine survives as structure in neutral. */
html[data-theme="light"] .detail-wrapper .routine-panel {
    background: var(--recess-a03);
    border-color: var(--border-default);
    border-left-color: var(--border-strong);
}

/* The supersede list's spine and the naming field, same reasoning. */
html[data-theme="light"] .detail-wrapper .supersede-list { border-left-color: var(--border-default); }
html[data-theme="light"] .detail-wrapper .hq-name-input { border-color: var(--border-default); }
html[data-theme="light"] .detail-wrapper .hq-name-input:focus { border-color: var(--focus-ring); }
html[data-theme="light"] .detail-wrapper .photo-thumb:hover { border-color: var(--border-strong); }

/* Every focus ring on these two pages, on one token. Several were --astra-accent-soft, which light
   still resolves to the pre-derivation #7c3aed. */
html[data-theme="light"] .detail-wrapper .ident-cust.link-value:focus-visible,
html[data-theme="light"] .detail-wrapper .ident-key.link-value:focus-visible,
html[data-theme="light"] .detail-wrapper .full-quote-btn:focus-visible,
html[data-theme="light"] .detail-wrapper .quote-more-btn:focus-visible,
html[data-theme="light"] .detail-wrapper .routine-open-btn:focus-visible,
html[data-theme="light"] .detail-wrapper .hq-pen:focus-visible,
html[data-theme="light"] .detail-wrapper .eq-field-row:focus-visible { outline-color: var(--focus-ring); }

/* ── RepairOrder.vue, as it renders on these two pages ──
   A shared component (src/components/RepairOrder.vue) and therefore not this cluster's file to
   edit — but its panel is the biggest tinted block on both pages, so the light half is corrected
   here, anchored under .detail-wrapper so no other host is touched. Its <style scoped> compiles to
   `.ro[data-v-…]` at (0,2,0); these are (0,3,0). ESCALATED: the same panel will need the same
   treatment wherever else it is hosted.

   The dark fill is a gradient of two violets, which §10 says never to touch — so it is not touched.
   Light gets a flat recess instead, which is what the gradient was approximating anyway. */
html[data-theme="light"] .detail-wrapper .ro {
    background: var(--recess-a03);
    border-color: var(--border-default);
}
html[data-theme="light"] .detail-wrapper .ro-title { color: var(--text-muted); }
html[data-theme="light"] .detail-wrapper .ro-amount,
html[data-theme="light"] .detail-wrapper .ro-sum.due { color: var(--text); }
html[data-theme="light"] .detail-wrapper .ro-group.additions { border-left-color: var(--border-strong); }
html[data-theme="light"] .detail-wrapper .ro-invoice-btn {
    background: var(--surface-3);
    border-color: var(--border-default);
    color: var(--text);
}
html[data-theme="light"] .detail-wrapper .ro-invoice-btn:hover {
    background: var(--surface-3-hover);
    border-color: var(--border-strong);
}
html[data-theme="light"] .detail-wrapper .ro-invoice-btn:focus-visible { outline-color: var(--focus-ring); }


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   /quote-drafts — .deck-wrapper

   The console and the history ledger. Selection here already runs through --surface-selected +
   --accent-bar (lx-row.picked), which is correct; what is left is the decorative tier.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* The two prose spines in the console sheet — the offer and "vad ni kom överens om". Same call as
   .qdd-msg's edge on the detail page: the rule is structure, so it goes neutral and the prose stops
   being framed in accent. */
html[data-theme="light"] .deck-wrapper .qd-offer,
html[data-theme="light"] .deck-wrapper .qd-agreed { border-left-color: var(--border-strong); }

/* Two tinted pills — "N nyare meddelanden — öppna chatten" and the arrivals count. Both are
   secondary actions; neither is the decision this console exists for (that is "Bekräfta & Skicka",
   which keeps its accent fill). */
html[data-theme="light"] .deck-wrapper .qd-newer,
html[data-theme="light"] .deck-wrapper .qd-arrivals {
    background: var(--surface-3);
    border-color: var(--border-default);
    color: var(--text);
}
html[data-theme="light"] .deck-wrapper .qd-newer:hover,
html[data-theme="light"] .deck-wrapper .qd-arrivals:hover {
    background: var(--surface-3-hover);
    border-color: var(--border-strong);
}

/* The origin chip on a history row. Violet as an identity hue, restating a word already printed. */
html[data-theme="light"] .deck-wrapper .qh-source.src-agent {
    color: var(--text-muted);
    background: var(--surface-3);
    border-color: var(--border-default);
}

html[data-theme="light"] .deck-wrapper .qd-link { color: var(--accent-solid); }
html[data-theme="light"] .deck-wrapper .qd-link:hover { color: var(--accent-solid-hover); }
html[data-theme="light"] .deck-wrapper .qd-chat:hover:not(:disabled) { border-color: var(--border-strong); }

html[data-theme="light"] .deck-wrapper .qd-body,
html[data-theme="light"] .deck-wrapper .qs-scroll { scrollbar-color: var(--border-strong) transparent; }
html[data-theme="light"] .deck-wrapper .qd-body::-webkit-scrollbar-thumb,
html[data-theme="light"] .deck-wrapper .qs-scroll::-webkit-scrollbar-thumb { background: var(--border-strong); }

/* ── ChatThreadBar — the orphan ────────────────────────────────────────────────────────────────
   Not in any Phase 7 cluster's file list, so nobody owned it, and it draws the two pill rails at the
   very top of every chat drawer. In light those were the most saturated objects on screen: a 30% and
   a 22% violet wash carrying near-white ink, so the *selected* pill read as a lavender block with
   pale text on it while its neighbours read as plain rows.

   Selection here is the same answer as everywhere else: a quiet wash, a leading bar, and ordinary
   ink. The scrollbar thumbs were translucent violet — decoration, not an action — and go neutral. */
html[data-theme="light"] .ctb-thread-pill.active {
    background: var(--surface-selected);
    border-color: var(--accent-hairline);
    color: var(--text);
    box-shadow: inset 3px 0 0 var(--accent-bar);
}
html[data-theme="light"] .ctb-service-pill.active {
    background: var(--surface-selected);
    border-color: var(--accent-hairline);
    color: var(--text);
    box-shadow: inset 3px 0 0 var(--accent-bar);
}
html[data-theme="light"] .ctb-service-pill.active .ctb-service-more {
    color: var(--text-secondary);
}
/* `.ctb-service-count` does not exist — the element is `.ctb-service-more` (ChatThreadBar.vue:21).
   The rule above styles it in the ACTIVE pill; this is the same span in the inactive one, so it
   takes the unqualified class. Written against a guessed name, it silently did nothing. */
html[data-theme="light"] .ctb-service-more {
    color: var(--text-muted);
}
html[data-theme="light"] .ctb-threads {
    scrollbar-color: var(--border-strong) var(--surface-3);
}
html[data-theme="light"] .ctb-threads::-webkit-scrollbar-thumb,
html[data-theme="light"] .ctb-threads:hover::-webkit-scrollbar-thumb {
    background: var(--border-strong);
}

/* The customer's letter is PAPER, not a well — overriding the mechanical mapping deliberately.
   `.qdd-msg` carries the prose the customer actually receives. The slab fix moved it from a mid-grey
   #afaeb8 to --well-a28's #cfcdda, which is legible (7.45:1) and correct by the ladder — and still
   wrong, because it makes the letter the heaviest block on the page. A letter wants to be the
   lightest thing on a document, the way a printed quote is white paper with rules around it.
   So it goes UP instead of down: a raised surface with a hairline and the accent kept only on its
   leading edge, which is where the eye should enter it. Raised by --elevation-1 rather than by a
   tint, because above --surface-3 the light theme has no lightness headroom left. */
html[data-theme="light"] .qdd-page .qdd-msg {
    background: var(--surface-3);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--elevation-1);
}


/* ═════════════════════════════════════════════════════════════════════════════════════════════
   THE STRUCTURAL PASS — detail / record pages
   ═════════════════════════════════════════════════════════════════════════════════════════════
   Everything above is the COLOUR pass. This is the pass that changes SHAPE, mapped from the signed
   -off prototype in `boards.css` §"WORK QUEUE — the light structural prototype" and the recipes in
   `_base.css`. Nothing here restates either file.

   THE ONE SENTENCE: on a record page, the record is the page. A record does not need a card around
   every field group, a capsule around every status word, or a tint behind every fact. It needs a
   heading, a rule, a row height, and one strong weight per pair.

   The scoping rule from the head of this file is unchanged and is not optional — `.glass-panel`,
   `.glass-section`, `.section-title`, `.detail-row`, `.badge`, `.feedback-msg`, `.status-badge` are
   each declared by half a dozen page sheets in four different clusters. Every rule below is anchored
   on `.detail-wrapper`, `.qhd-wrapper`, `.qdd-page` or `.deck-wrapper`.
   ═════════════════════════════════════════════════════════════════════════════════════════════ */


/* ── D1. /job-detail: THE GLASS PANEL IS THE PAGE, SO IT STOPS BEING A CARD ────────────────────
   `.glass-panel` is --surface-1 + a 28px backdrop blur + a 40px-blur drop shadow + a lit top edge +
   a 20px radius. In light: the filter token is `none`, the shadow is a smudge under a white box on
   an off-white ground, and the lit edge composites to nothing. What is left is a big rounded
   rectangle whose only real job — separating the record from the ground — is done by one hairline.

   Surface, one keyline, --r-card. It must keep the border: white on #f6f8fa with no hairline is the
   light theme's silent failure, where the element is simply absent and nothing throws. */
html[data-theme="light"] .detail-wrapper .glass-panel {
    background: var(--surface);
    border: 1px solid var(--keyline);
    border-radius: var(--r-card);
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* The rule between the card's blocks was a gradient fading to transparent at both ends. A rule fades
   on near-black because a hard line there reads as a scratch; on white it reads as a rendering
   artefact. Flat keyline. */
html[data-theme="light"] .detail-wrapper .divider {
    background: var(--keyline);
}


/* ── D2. THE JOB STATUS BADGE TAKES THE BADGE GRAMMAR ──────────────────────────────────────────
   `.status-badge` is in _base §1's selector list, so it already has the rectangle, the 11px and the
   neutral rung. What _base cannot know are the seven `.status-badge.<state>` variants, which set
   their own fill/border/ink at (0,2,0) — the same weight as _base's rule and LATER in document
   order, because page sheets are injected at runtime. So each variant is re-pointed here at (0,3,0),
   and each gets the mandatory shape-distinct glyph.

   The verdict rule decides which of them keeps a hue. `booked` and `awaiting-checkin` are the
   ORDINARY states of a job — nearly every record on the board is one or the other — and a colour
   that is almost always on screen has stopped carrying information. They go neutral. `canceled` is
   also neutral, and that is the page sheet's own reasoning kept intact: nothing went wrong, the
   visit simply is not happening. What is left with a hue is work happening now, work finished, and
   a car nobody has collected. */
html[data-theme="light"] .detail-wrapper .status-badge {
    box-shadow: none;
    text-transform: none;
    letter-spacing: 0;
}
html[data-theme="light"] .detail-wrapper .status-badge::before {
    font-size: 1em;
    line-height: 1;
}

html[data-theme="light"] .detail-wrapper .status-badge.booked,
html[data-theme="light"] .detail-wrapper .status-badge.awaiting-checkin,
html[data-theme="light"] .detail-wrapper .status-badge.canceled {
    background: var(--surface-inset);
    border: 1px solid var(--keyline-strong);
    color: var(--ink-dim);
}
html[data-theme="light"] .detail-wrapper .status-badge.booked::before,
html[data-theme="light"] .detail-wrapper .status-badge.awaiting-checkin::before,
html[data-theme="light"] .detail-wrapper .status-badge.canceled::before { content: "•"; }

html[data-theme="light"] .detail-wrapper .status-badge.ongoing,
html[data-theme="light"] .detail-wrapper .status-badge.in-progress,
html[data-theme="light"] .detail-wrapper .status-badge.awaiting-pickup {
    background: var(--st-expected-soft);
    border: 1px solid var(--st-expected-line);
    color: var(--st-expected);
}
html[data-theme="light"] .detail-wrapper .status-badge.ongoing::before,
html[data-theme="light"] .detail-wrapper .status-badge.in-progress::before,
html[data-theme="light"] .detail-wrapper .status-badge.awaiting-pickup::before { content: "!"; }

html[data-theme="light"] .detail-wrapper .status-badge.finished {
    background: var(--st-logged-soft);
    border: 1px solid var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .detail-wrapper .status-badge.finished::before { content: "✓"; }


/* ── D3. THE STATUS BAND ───────────────────────────────────────────────────────────────────────
   The page's primary object: its own surface, tinted by the state it reports. It drives itself off
   three local custom properties per variant, which is the good news — the band's geometry is
   already right, and only the three values need re-pointing, in one place each.

   Four of the six variants paint a raw dark-theme channel (`--c-indigo-c4b5fd`, `--c-orange-fbbf24`,
   `rgba(--sf-rgb, .72)`, `--c-blue-93c5fd`), which in light are a saturated pigment or an ink-alpha
   that composites to a grey haze. And the same verdict rule as D2 applies: `booked`,
   `awaiting-checkin` and `canceled` are the ordinary cases and go neutral, so the band only carries
   a hue when the state is one you would act on. */
html[data-theme="light"] .detail-wrapper .status-band {
    --band-ink: var(--ink-dim);
    --band-soft: var(--surface-inset);
    --band-line: var(--keyline-strong);
}
html[data-theme="light"] .detail-wrapper .status-band.in-progress,
html[data-theme="light"] .detail-wrapper .status-band.ongoing,
html[data-theme="light"] .detail-wrapper .status-band.awaiting-pickup {
    --band-ink: var(--st-expected);
    --band-soft: var(--st-expected-soft);
    --band-line: var(--st-expected-line);
}
html[data-theme="light"] .detail-wrapper .status-band.finished {
    --band-ink: var(--st-logged);
    --band-soft: var(--st-logged-soft);
    --band-line: var(--st-logged-line);
}
/* The band's caption is a caption; the state is the one strong thing in it. */
html[data-theme="light"] .detail-wrapper .sb-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
    text-transform: uppercase;
    color: var(--ink-muted);
}
html[data-theme="light"] .detail-wrapper .sb-value {
    font-weight: var(--fw-strong);
    color: var(--ink);
}


/* ── D4. THE FACT GRID: LABEL IS A CAPTION, VALUE IS THE RECORD ────────────────────────────────
   `.info-label` is 11px uppercase at `rgba(--ch-indigo-c4b5fd, .62)` — violet-at-alpha doing a text
   job, the same defect the colour pass measured at 2.15:1 on the quote history. It is already
   neutralised above; here it goes onto the eyebrow rung so a field caption on this page is the same
   object as a field caption on the Work Queue and on /vehicle-detail.

   And the value beside it takes the one --fw-strong in the block. That is the whole point of
   dropping 700 in light: emphasis moves to the ink ladder and to isolation, and what is left of
   weight is spent on the ONE thing per container that the reader came for. */
html[data-theme="light"] .detail-wrapper .info-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
    color: var(--ink-muted);
}
/* Same call as /vehicle-detail's `.detail-label::before`: with a caption above the value rather than
   across a wide card from it, the 4px tick is a bullet in front of every caption and anchors a pair
   that is already one object. */
html[data-theme="light"] .detail-wrapper .info-label::before { display: none; }

html[data-theme="light"] .detail-wrapper .info-value {
    font-weight: var(--fw-strong);
    color: var(--ink);
}
/* Three exemptions, each because the slot is not the record's answer. The raw key is a support
   artefact and stays proportional and quiet; a muted value is muted on purpose; and a link's colour
   is set by the accent block above and its weight should not shout over the values around it. */
html[data-theme="light"] .detail-wrapper .info-value.mono {
    font-weight: var(--fw-body);
    color: var(--ink-muted);
}
html[data-theme="light"] .detail-wrapper .info-value.muted {
    font-weight: var(--fw-body);
    color: var(--ink-muted);
}
html[data-theme="light"] .detail-wrapper .info-value.link-value { font-weight: var(--fw-label); }

/* The identity line under the heading, and every figure on the page that sits in a compared column.
   _base §5 scopes tabular figures to money/time/count rather than applying them globally, because
   tnum in prose reads as a ransom note — so the slots are named. */
html[data-theme="light"] .detail-wrapper .ident-dur,
html[data-theme="light"] .detail-wrapper .hq-amount,
html[data-theme="light"] .detail-wrapper .hq-total,
html[data-theme="light"] .detail-wrapper .info-block.price .info-value {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}


/* ── D5. THE QUOTE BLOCKS ──────────────────────────────────────────────────────────────────────
   `.quote-card` is a recess inside the record. In light a recess is --surface-inset with a real
   edge, at a radius smaller than its container's (the inner-radius rule), and 14px is not smaller
   than the card's --r-card. */
html[data-theme="light"] .detail-wrapper .quote-card {
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    border-radius: var(--r-control);
}

/* Three capsules on the quote header: how many quotes, what equipment it needs, and the quote's own
   state. All three are rectangles now. The count and the equipment chip are DATA — a number and a
   noun — so they take the neutral rung and no glyph; only a state earns a glyph, and `.quote-status`
   prints its state as a word rather than as a chip, so it takes ink rather than a fill. */
html[data-theme="light"] .detail-wrapper .quote-count,
html[data-theme="light"] .detail-wrapper .eq-chip {
    border-radius: 4px;
    padding: 2px 6px;
    font-size: var(--t-micro);
    font-weight: var(--fw-label);
    letter-spacing: 0;
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    color: var(--ink-dim);
    box-shadow: none;
}
html[data-theme="light"] .detail-wrapper .quote-status {
    font-weight: var(--fw-label);
    color: var(--ink-dim);
}

/* The difficulty ramp. It is deliberately NOT the status palette — see the --diff-* block in
   app.css — so the hues stay; only the capsule goes. ESCALATED, and it is the one real token defect
   this cluster found: `--diff-1` is declared `#94a3b8` in the LIGHT block (app.css:2336), which the
   frozen spec bans by name at §3 and §12.2 — it measures 2.56:1 on white and is used here as TEXT
   inside a 10px badge. app.css is the lead's file; this cannot be fixed from a cluster sheet, and
   pointing the badge at a different family would flatten a distinction the page makes on purpose. */
html[data-theme="light"] .detail-wrapper .diff-badge {
    border-radius: 4px;
    padding: 2px 6px;
    font-size: var(--t-micro);
    font-weight: var(--fw-label);
    letter-spacing: 0;
    text-transform: none;
    box-shadow: none;
}

/* A quote's own line items are a ledger: the name reads down the left, the amount down the right. */
html[data-theme="light"] .detail-wrapper .hq-line {
    min-height: var(--row-h-compact);
    border-bottom: 1px solid var(--keyline);
}
html[data-theme="light"] .detail-wrapper .hq-line:last-child { border-bottom-color: transparent; }
html[data-theme="light"] .detail-wrapper .hq-amount {
    font-weight: var(--fw-label);
    color: var(--ink);
}

/* The pass/fail line under a save. Off the raw emerald/red channels onto the measured status pair,
   and a rectangle rather than a 10px lozenge floating centred in the card. */
html[data-theme="light"] .detail-wrapper .feedback-msg,
html[data-theme="light"] .qhd-wrapper .feedback-msg {
    border-radius: var(--r-control);
    text-align: left;
    color: var(--st-logged);
    background: var(--st-logged-soft);
    border: 1px solid var(--st-logged-line);
}
html[data-theme="light"] .detail-wrapper .feedback-msg.error,
html[data-theme="light"] .qhd-wrapper .feedback-msg.error {
    color: var(--st-breach);
    background: var(--st-breach-soft);
    border-color: var(--st-breach-line);
}


/* ═════════════════════════════════════════════════════════════════════════════════════════════
   D6. /quote-history-detail — the same record, read-only
   ═════════════════════════════════════════════════════════════════════════════════════════════ */

/* The section cards. Same call as /job-detail's `.glass-panel` and /vehicle-detail's
   `.glass-section`, and named separately because `.glass-section` is declared by three sheets in two
   clusters. */
html[data-theme="light"] .qhd-wrapper .glass-section {
    background: var(--surface);
    border: 1px solid var(--keyline);
    border-radius: var(--r-card);
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

html[data-theme="light"] .qhd-wrapper .section-title,
html[data-theme="light"] .qhd-wrapper .sec-title,
html[data-theme="light"] .qhd-wrapper .wait-status-label,
html[data-theme="light"] .qhd-wrapper .total-toggle-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
    color: var(--ink-muted);
}

/* The Översikt rows. A table, for the same reason as everywhere else in this cluster: the reader is
   scanning a column of captions for one value. */
html[data-theme="light"] .qhd-wrapper .detail-rows { gap: 0; }
html[data-theme="light"] .qhd-wrapper .detail-row {
    min-height: var(--row-h);
    padding: 9px 0;                     /* derived from --row-h — not on the space scale */
    border-bottom: 1px solid var(--keyline);
}
html[data-theme="light"] .qhd-wrapper .detail-row:last-child { border-bottom-color: transparent; }
html[data-theme="light"] .qhd-wrapper .detail-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
}
html[data-theme="light"] .qhd-wrapper .detail-label::before { display: none; }
html[data-theme="light"] .qhd-wrapper .detail-value {
    font-weight: var(--fw-strong);
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}
html[data-theme="light"] .qhd-wrapper .detail-value.mono {
    font-weight: var(--fw-body);
    color: var(--ink-muted);
    font-variant-numeric: normal;
}

/* The quote's own verdict, at the top right of the page. Badge grammar, with the glyph that keeps
   "bekräftad" and "avvisad" apart for a reader who cannot separate the two fills — under
   deuteranopia they are two olives at ΔE 0.027, and the TEXT at 0.040. */
html[data-theme="light"] .qhd-wrapper .qh-status {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    border-radius: 4px;
    padding: 2px 6px;
    font-size: var(--t-micro);
    font-weight: var(--fw-label);
    line-height: 1.4;
    letter-spacing: 0;
    text-transform: none;
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    color: var(--ink-dim);
}
html[data-theme="light"] .qhd-wrapper .qh-status.st-confirmed {
    background: var(--st-logged-soft);
    border-color: var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .qhd-wrapper .qh-status.st-confirmed::before { content: "✓"; line-height: 1; }
html[data-theme="light"] .qhd-wrapper .qh-status.st-rejected {
    background: var(--st-breach-soft);
    border-color: var(--st-breach-line);
    color: var(--st-breach);
}
html[data-theme="light"] .qhd-wrapper .qh-status.st-rejected::before { content: "✕"; line-height: 1; }
/* Replaced, not refused: it is not a failure, it is a version. Neutral, with the neutral dot. */
html[data-theme="light"] .qhd-wrapper .qh-status.st-superseded {
    background: var(--surface-inset);
    border-color: var(--keyline);
    color: var(--ink-dim);
}
html[data-theme="light"] .qhd-wrapper .qh-status.st-superseded::before { content: "•"; line-height: 1; }

/* The counter-response state, printed as a coloured WORD inside a sentence rather than as a chip.
   Four raw channels, two of which (`--c-blue-cbd5e1`, `--c-blue-94a3b8`) are the pale slates the
   spec bans on a white ground. Colour alone in running text is the weakest possible carrier, so the
   two ordinary states go to ink and only accepted / declined keep a hue. */
html[data-theme="light"] .qhd-wrapper .unavail-cr {
    font-weight: var(--fw-label);
    color: var(--ink-dim);
}
html[data-theme="light"] .qhd-wrapper .unavail-cr.cr-pending,
html[data-theme="light"] .qhd-wrapper .unavail-cr.cr-expired { color: var(--ink-muted); }
html[data-theme="light"] .qhd-wrapper .unavail-cr.cr-accepted { color: var(--st-logged); }
html[data-theme="light"] .qhd-wrapper .unavail-cr.cr-declined { color: var(--st-breach); }

/* The waiting panel. Its "waiting" tint is a raw orange channel with a raw amber ink on it; the
   measured pair, and the value takes the strong weight rather than the hue doing that job too. */
html[data-theme="light"] .qhd-wrapper .wait-status {
    border-radius: var(--r-control);
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
}
html[data-theme="light"] .qhd-wrapper .wait-status.waiting {
    background: var(--st-expected-soft);
    border-color: var(--st-expected-line);
}
html[data-theme="light"] .qhd-wrapper .wait-status.waiting .wait-status-ic,
html[data-theme="light"] .qhd-wrapper .wait-status.waiting .wait-status-value { color: var(--st-expected); }
html[data-theme="light"] .qhd-wrapper .wait-status-value { font-weight: var(--fw-strong); color: var(--ink); }
html[data-theme="light"] .qhd-wrapper .wait-fact-value { font-variant-numeric: tabular-nums; }
html[data-theme="light"] .qhd-wrapper .wait-fact-value.date-overdue { color: var(--st-breach); }

/* The overdue marker beside a date: a 999px lozenge in a raw red channel. Rectangle, measured
   tokens, and the cross that makes it survive a colour-vision failure. */
html[data-theme="light"] .qhd-wrapper .wait-overdue-tag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    border-radius: 4px;
    padding: 2px 6px;
    font-size: var(--t-micro);
    font-weight: var(--fw-label);
    letter-spacing: 0;
    background: var(--st-breach-soft);
    border-color: var(--st-breach-line);
    color: var(--st-breach);
}
html[data-theme="light"] .qhd-wrapper .wait-overdue-tag::before { content: "✕"; line-height: 1; }

/* The equipment chips and the origin chip: nouns, so a neutral rectangle and no glyph. */
html[data-theme="light"] .qhd-wrapper .qhd-eq-chip,
html[data-theme="light"] .qhd-wrapper .qh-source,
html[data-theme="light"] .deck-wrapper .qh-source {
    border-radius: 4px;
    padding: 2px 6px;
    font-size: var(--t-micro);
    font-weight: var(--fw-label);
    letter-spacing: 0;
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    color: var(--ink-dim);
}

/* The two disclosures. A tile that folds is a control, so it takes the control radius; the total
   keeps its position and its figure as the emphasis, which is what it had before a violet slab was
   added on top to say the same thing a third time. */
html[data-theme="light"] .qhd-wrapper .sec-toggle,
html[data-theme="light"] .qhd-wrapper .total-toggle {
    border-radius: var(--r-control);
    min-height: var(--row-h-comfy);
}
html[data-theme="light"] .qhd-wrapper .total-toggle-value {
    font-weight: var(--fw-strong);
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}
html[data-theme="light"] .qhd-wrapper .qb-row,
html[data-theme="light"] .qhd-wrapper .qb-row .num { font-variant-numeric: tabular-nums; }
html[data-theme="light"] .qhd-wrapper .qb-row.grand {
    border-top-color: var(--keyline-strong);
    font-weight: var(--fw-strong);
}

/* The rejection quotation. It is the customer's own words about why the work is not happening — a
   real negative outcome, so the red stays; it moves off the raw `--ch-red-f87171` channel onto the
   measured trio and off a 12px radius onto the card rung. */
html[data-theme="light"] .qhd-wrapper .reject-callout {
    border-radius: var(--r-control);
    background: var(--st-breach-soft);
    border: 1px solid var(--st-breach-line);
    border-left: 3px solid var(--st-breach);
}


/* ═════════════════════════════════════════════════════════════════════════════════════════════
   D7. /quote-draft-detail — the document
   ═════════════════════════════════════════════════════════════════════════════════════════════
   This page is largely built on the `--fin-*` type and rule system, which the MONEY cluster owns, so
   it is touched here only where a value is NOT a --fin-* token: the zone panels (raw --sf-rgb), the
   eyebrow geometry, and the figures. Anything reading --fin-rule / --fin-ink / --fin-label is left
   alone deliberately — two clusters restyling one ladder is how a second type scale gets born. */

/* The three zone panels. `rgba(--sf-rgb, 0.028)` over a `rgba(--sf-rgb, 0.09)` border: in light
   --sf-rgb is the INK channel, so the fill is a 2.8% grey wash and the border a 9% grey line — a
   panel that is nearly there. The inset rung with a real keyline says the same thing and says it at
   a measured value. */
html[data-theme="light"] .qdd-page .qdd-zone {
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    border-radius: var(--r-card);
}
html[data-theme="light"] .qdd-page .qdd-zone-head {
    border-bottom-color: var(--keyline);
}

/* The three eyebrows. Their colour was corrected above; this puts their GEOMETRY on the shared rung
   — 0.22em and 0.12em were two different trackings for one object, on one page. */
html[data-theme="light"] .qdd-page .qdd-title,
html[data-theme="light"] .qdd-page .qdd-zone-head,
html[data-theme="light"] .qdd-page .qdd-group-name,
html[data-theme="light"] .qdd-page .qdd-pay-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
}

/* Every figure on a quote sits in a column that is read downward. */
html[data-theme="light"] .qdd-page .qdd-line-hours,
html[data-theme="light"] .qdd-page .qdd-line-amt,
html[data-theme="light"] .qdd-page .qdd-group-hours,
html[data-theme="light"] .qdd-page .qdd-group-sum,
html[data-theme="light"] .qdd-page .qdd-money,
html[data-theme="light"] .qdd-page .qdd-trow,
html[data-theme="light"] .qdd-page .qdd-pay-value,
html[data-theme="light"] .qdd-page .qdd-hl-amount,
html[data-theme="light"] .qdd-page .qdd-hl-when {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* The payable total is the one --fw-strong in the totals block; the rows above it are context. */
html[data-theme="light"] .qdd-page .qdd-pay-value {
    font-weight: var(--fw-strong);
    color: var(--ink);
}

/* A line item is a row in a ledger, so it gets a row height rather than only padding. The rule under
   it is --fin-rule and stays the money cluster's. */
html[data-theme="light"] .qdd-page .qdd-line {
    min-height: var(--row-h);
}

/* The blocker sentence sits on the foot bar and is the one thing stopping a send: a state, so it
   keeps the amber ink already assigned above and gains the shape that makes it survive a
   colour-vision failure. `.qdd-blockers-dot` is a bare disc doing exactly the job _base warns a
   coloured dot cannot do alone, so the bang goes on the sentence rather than on the dot. */
html[data-theme="light"] .qdd-page .qdd-blockers::before {
    content: "!";
    margin-right: 4px;
    font-weight: var(--fw-label);
    line-height: 1;
}
html[data-theme="light"] .qdd-page .qdd-blockers-dot { display: none; }


/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   P2 — JOB DETAIL, REBUILT ON THE MEASURED SHELL
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   Read at 1900px, which is where the shop owner judges it, the page is three floating objects and a
   lot of nothing: a bordered record column in the middle, a bordered context rail hanging in the
   left void, and inside the record two more filled boxes — the status band and the routine panel.
   Depth is spent four times on a page where exactly one thing actually floats.

   THE RULE THIS PASS APPLIES, and it settles all four at once: a container has EITHER a border OR an
   elevation, never both, and whatever is inside it has NEITHER. Depth belongs to the outermost
   object only. On this page that object is the context rail — it is `position: fixed` and genuinely
   sits over the content — so the rail keeps its elevation and everything else stands down.

   NOTE ON SCOPE: `.detail-wrapper` is ALSO the root of FinishedJobDetailDesktop.vue, so every rule
   here lands on both pages. That is wanted for the shared furniture below. And six overlays —
   finish, skill-warn, edit, cancel, photo lightbox and the chat drawer — TELEPORT OUT of the wrapper
   to <body>, so anything scoped to `.detail-wrapper` cannot reach them; they are handled by their
   own roots at the end of this section. Scoping an overlay to its page wrapper is a rule that parses
   cleanly and applies to nothing, which cost a full pass on the Jobs overlays earlier today. */

/* ── 1. THE RECORD STOPS BEING A CARD ──────────────────────────────────────────────────────────
   The earlier pass gave `.glass-panel` a surface, a keyline and `--r-card` on the argument that
   "white on #f6f8fa with no hairline is the light theme's silent failure". That was right when the
   page ground was `#f6f8fa`. It is not any more: the desktop page is `--surface`, white, so the card
   is white-on-white and the hairline is the ONLY thing drawing it — which is the definition of a
   box drawn for its own sake.

   The record is the page. Its internal dividers already separate its blocks, and they were flattened
   to a keyline in the same earlier pass, so the structure survives the frame's removal intact. */
html[data-theme="light"] .detail-wrapper .glass-panel {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/* ── 2. THE STATUS BAND IS THE PAGE'S HEADLINE, NOT A TILE ─────────────────────────────────────
   A filled box with a leading bar, holding the one fact the page exists to state and the two actions
   that change it. Filled, it reads as an alert about the record; flat, it reads as the record's
   state, which is what it is. The leading bar stays — it is the band's only structural mark and it
   is how the hue arrives when the state is one worth acting on. */
html[data-theme="light"] .detail-wrapper .status-band {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: inset 3px 0 0 0 var(--band-line);
    padding: 12px 0 12px 16px;
}

/* ── 3. THE ROUTINE PANEL IS A SECTION ─────────────────────────────────────────────────────────
   `.rsum` is a grey filled card inside the record, holding prose and two links. Grey fill on a white
   surface is the universal read-only signal, and this block is editable from two of its own
   controls. A section: an eyebrow, a hairline above it, and the space it needs. */
html[data-theme="light"] .detail-wrapper .rsum,
html[data-theme="light"] .detail-wrapper .routine-panel,
html[data-theme="light"] .detail-wrapper .quote-card,
html[data-theme="light"] .detail-wrapper .quote-section {
    background: none;
    background-image: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

html[data-theme="light"] .detail-wrapper .rsum {
    padding: 0;
}

/* `Skriv om` is a capsule. Roundness carries meaning here: a full round is reserved for the one
   interactive filter shape, so a capsule that is not a filter reads as a button you cannot press.
   The measured control edge instead — a 1px navy spread shadow, not a border, with the layer it does
   not yet need pre-declared at alpha 0 so the hover interpolates rather than snapping. */
html[data-theme="light"] .detail-wrapper .rsum-pen,
html[data-theme="light"] .detail-wrapper .rsum-act,
html[data-theme="light"] .detail-wrapper .rsum-note-add {
    background: var(--surface);
    background-image: none;
    border: 0;
    border-radius: var(--r-control);
    color: var(--ink-dim);
    box-shadow:
        0 0 0 1px rgba(var(--edge-rgb), 0.10),
        0 1px 1px rgba(0, 0, 0, 0.08),
        0 0 0 0 transparent;
    transition: box-shadow 0.08s ease-in, color 0.08s ease-in;
}

html[data-theme="light"] .detail-wrapper .rsum-pen:hover,
html[data-theme="light"] .detail-wrapper .rsum-act:hover,
html[data-theme="light"] .detail-wrapper .rsum-note-add:hover {
    background: var(--surface);
    color: var(--ink);
    box-shadow:
        0 0 0 1px rgba(var(--edge-rgb), 0.10),
        0 1px 1px rgba(0, 0, 0, 0.10),
        0 3px 9px rgba(var(--edge-rgb), 0.10);
}

/* ── 4. EYEBROWS GO SENTENCE CASE ──────────────────────────────────────────────────────────────
   Eleven uppercase tracked labels on one page — Tjänst, Extra / anteckningar, Svårighetsgrad,
   Önskad mekaniker, Rutin, Verkstadens anteckning, Foton, Specialutrustning, Nuvarande status, Den
   här bilen, Manuell bokning. The measured stylesheet contains NINE letter-spacing declarations in
   3.6 MB and not one is on a section label; their own shell is actively shrinking the old 13px
   uppercase header down to 11px/500 sentence case.

   In Swedish it costs more than in English: SPECIALUTRUSTNING is seventeen characters of caps
   labelling a row that says "Inga valda". */
html[data-theme="light"] .detail-wrapper .info-label,
html[data-theme="light"] .detail-wrapper .sb-label,
html[data-theme="light"] .detail-wrapper .quote-head,
html[data-theme="light"] .detail-wrapper .rsum-title,
html[data-theme="light"] .detail-wrapper .rsum-note-label,
html[data-theme="light"] .detail-wrapper .badge,
html[data-theme="light"] .crail-head {
    font-size: 0.6875rem;
    font-weight: var(--fw-label);
    letter-spacing: 0;
    text-transform: none;
    color: var(--ink-muted);
}

/* The page eyebrow is a label, so it stops wearing a chip: a filled rectangle around a word is the
   costume of a status, and this one only repeats which page you are on. */
html[data-theme="light"] .detail-wrapper .detail-header .badge {
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0;
    border-radius: 0;
}

/* ── 5. THE NOTICE — a wash, not a slab ────────────────────────────────────────────────────────
   "Detta är en schemalagd rutin" arrives as a full-width mint block with a border. Two things are
   wrong with it and only one is colour.

   Severity, measured: a 5% wash of the hue over an opaque white layer, no border, no left bar, no
   icon on a page banner. The warning fill sits at 1.065:1 against white — a WEAKER departure from
   white than plain grey is — and all three severities land within 0.007 of each other, so severity
   is carried by HUE at near-zero chroma and never by intensity.

   And this is not a success in the first place. It states how the visit is priced. Green for
   "everything worked" on a line that means "read this before you finish the job" spends the one
   colour a page gets on the wrong sentence. It goes neutral; the hue stays available for the case
   that is actually wrong. */
html[data-theme="light"] .detail-wrapper .feedback-msg:not(.error) {
    background: var(--surface-inset);
    border: 0;
    border-radius: var(--r-control);
    color: var(--ink-dim);
    box-shadow: inset 0 0 0 1px var(--keyline);
}

/* ── 6. AVBOKA — destructive, and therefore quiet ──────────────────────────────────────────────
   Bare red text floating at the bottom right of the record. Red text with no container is the
   vocabulary of an error message, not of a control, so the one genuinely irreversible action on the
   page is also the only one with no affordance at all.

   It takes the neutral control edge and keeps the red for the hover, where the intent is already
   formed. That is the same move the measured spec makes on an invalid field: the accusation is held
   back until it is useful. */
html[data-theme="light"] .detail-wrapper .btn-cancel-quiet {
    background: var(--surface);
    border: 0;
    border-radius: var(--r-control);
    padding: 6px 16px;
    min-height: 36px;
    color: var(--ink-muted);
    font-weight: var(--fw-label);
    box-shadow:
        0 0 0 1px rgba(var(--edge-rgb), 0.10),
        0 1px 1px rgba(0, 0, 0, 0.08),
        0 0 0 0 transparent;
    transition: box-shadow 0.08s ease-in, color 0.08s ease-in;
}

html[data-theme="light"] .detail-wrapper .btn-cancel-quiet:hover {
    background: var(--surface);
    color: var(--st-breach);
    box-shadow:
        0 0 0 1px rgba(var(--edge-rgb), 0.10),
        0 1px 1px rgba(0, 0, 0, 0.10),
        0 3px 9px rgba(var(--edge-rgb), 0.10);
}

/* ── 7. THE CONTEXT RAIL KEEPS ITS DEPTH, BECAUSE IT IS THE ONE THING THAT FLOATS ──────────────
   Everything above stood down so that this can stand up. `.crail` is `position: fixed`, docked to
   the left edge and genuinely over the content, which makes it the outermost object on the page and
   the one place depth is spent. `_base.css` already gives it contact rather than the 44px lift it
   wore in dark, and the card radius rather than 18px.

   What was missing is the rail's INTERIOR. The rows had no light rules at all — the dotted leader,
   the state dot, the value — so a rail that is now the page's only raised surface was carrying
   whatever the dark sheet left behind. */
/* DELETED 2026-08-11: `.crail-rule` — the name exists nowhere in `src/`, not in
   `ContextRail.css` and not in any template; the rail's separator is `.crow`'s own bottom keyline. */

html[data-theme="light"] .crow-label {
    color: var(--ink-dim);
}

html[data-theme="light"] .crow-value {
    color: var(--ink-muted);
    font-weight: var(--fw-label);
}

html[data-theme="light"] .crow:hover {
    background: var(--surface-inset);
}

/* An absent row is absent, not broken. It recedes with ink rather than with a second surface. */
html[data-theme="light"] .crow.is-absent .crow-label,
html[data-theme="light"] .crow.is-absent .crow-value {
    color: var(--ink-muted);
}

/* The dots. Three tones on a rail this small is two too many when the row's own text already says
   what it is; the hue is kept for the one that means "act on this". */
html[data-theme="light"] .crow-dot.accent {
    background: var(--accent);
}

html[data-theme="light"] .crow-dot.ok {
    background: var(--meter-ok);
}

html[data-theme="light"] .crow-dot.warn {
    background: var(--meter-warn);
}

/* ── 8. P2b — WHAT THE SECOND SCREENSHOT SHOWED ────────────────────────────────────────────────
   1. `Manuell bokning` stayed shouting, because that string is CAPITALISED IN THE STRING TABLE —
      `text-transform: none` had nothing to undo. Lowercase the whole label, then raise the first
      letter. Applied by name to a two-word page label, never broadly: it would mangle an acronym. */
html[data-theme="light"] .detail-wrapper .detail-header .badge {
    text-transform: lowercase;
}

html[data-theme="light"] .detail-wrapper .detail-header .badge::first-letter {
    text-transform: uppercase;
}

/* 2. Two pencils were being drawn by the operating system. `✎` is U+270E — a TEXT-presentation
      codepoint that Windows and Android still resolve through the emoji font, so the same character
      arrives flat on one machine and full-colour on another. Both live on dedicated elements, which
      is what makes the mask substitution clean: suppress the glyph, draw the token through
      `currentColor` so it is exactly the colour of the label beside it.

      The markup cannot be edited — those files are shared with dark — so this is suppress-and-
      replace rather than a template change. */
html[data-theme="light"] .detail-wrapper .rsum-pen {
    font-size: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

html[data-theme="light"] .detail-wrapper .rsum-pen::after {
    content: "";
    width: 13px;
    height: 13px;
    background-color: currentColor;
    mask-image: var(--icon-pencil);
    -webkit-mask-image: var(--icon-pencil);
    mask-size: contain;
    -webkit-mask-size: contain;
    mask-repeat: no-repeat;
    -webkit-mask-repeat: no-repeat;
    mask-position: center;
    -webkit-mask-position: center;
}

/* The inline `.static` pencil sits inside a labelled button, so it is decoration next to a word
   rather than a control of its own — no edge, and a little air before the label. */
html[data-theme="light"] .detail-wrapper .rsum-note-add .rsum-pen.static {
    background: none;
    box-shadow: none;
    padding: 0;
    margin-right: 6px;
}

/* 3. Two buttons kept a 999px radius. Roundness carries meaning: a full round is reserved for the
      one interactive filter shape, so a capsule that is not a filter reads as a button you cannot
      press. 4px is the measured ceiling for everything else. */
html[data-theme="light"] .detail-wrapper .routine-open-btn,
html[data-theme="light"] .detail-wrapper .full-quote-btn,
html[data-theme="light"] .detail-wrapper .photos-section button,
html[data-theme="light"] .detail-wrapper .supersede-toggle,
html[data-theme="light"] .detail-wrapper .quote-more-btn {
    border-radius: var(--r-control);
}

/* `::first-letter` only applies to elements with a BLOCK container, and `.badge` is inline-flex —
   so the lowercase landed and the capital never did, turning "MANUELL BOKNING" into "manuell
   bokning". Inline-block gives the pseudo-element something to attach to without changing how the
   label sits in the header row. */
html[data-theme="light"] .detail-wrapper .detail-header .badge {
    display: inline-block;
}

/* ── 9. THE EDIT SHEET ─────────────────────────────────────────────────────────────────────────
   Ten uppercase tracked field labels stacked down a 360px column — Datum, Starttid, Sluttid,
   Administrativ tid efter, Kundnamn, Telefon, Fordon, Tjänst, Extra / anteckningar, Uppskattat pris.
   A form is the place where shouting costs most: every label is read, none of them is a heading, and
   the tracking widens a column that is already the narrowest thing on screen.

   NOT scoped to `.detail-wrapper` — this sheet teleports to <body>, so a wrapper-scoped rule would
   parse cleanly and reach nothing. */
html[data-theme="light"] .modal-panel .modal-field > label,
html[data-theme="light"] .mdl-sheet .mdl-label {
    font-size: 0.6875rem;
    font-weight: var(--fw-label);
    letter-spacing: 0;
    text-transform: none;
    text-align: left;
    color: var(--ink-muted);
}

/* The lift toggle is a field group wearing the costume of a card: a bordered, filled frame whose
   only content is a switch and a word. The control has an edge because it is a control; the group
   around it is a label and some space. */
html[data-theme="light"] .modal-panel .lift-toggle,
html[data-theme="light"] .modal-panel .diff-toggle {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
}

/* ── 10. P2b — THE REPAIR ORDER AND THE QUOTE CARDS ────────────────────────────────────────────
   Only visible on a job that actually carries quotes, which is why it survived the first pass: the
   booking I had open was a routine visit and rendered neither block.

   `.ro` is a grey filled, bordered card sitting inside the record — a second surface holding the one
   table on the page. Same nesting rule as everywhere else: the record is the surface, and what is
   inside it separates with space and a hairline. The group's own dividers already do that work, so
   removing the frame costs nothing and returns the totals to the page. */
html[data-theme="light"] .detail-wrapper .ro {
    background: none;
    background-image: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
}

/* Arbetsorder, Ursprungligt arbete, Aktuell — three more uppercase tracked labels, one of them
   (`Aktuell`) doing status work on a quote row. A status on a row is a badge; a label above a table
   is an eyebrow. Both are 11px/500 sentence case, and neither is a heading that needs a hue. */
html[data-theme="light"] .detail-wrapper .ro-title,
html[data-theme="light"] .detail-wrapper .ro-group-label,
html[data-theme="light"] .detail-wrapper .quote-status,
html[data-theme="light"] .detail-wrapper .quote-meta,
html[data-theme="light"] .detail-wrapper .ro-auth {
    font-size: 0.6875rem;
    font-weight: var(--fw-label);
    letter-spacing: 0;
    text-transform: none;
    color: var(--ink-muted);
}

/* The order's rows keep one hairline each and the total keeps the weight — a totals row is the one
   line in a table that is allowed to be heavier than the rest, because it is what the table is for. */
html[data-theme="light"] .detail-wrapper .ro-line {
    box-shadow: inset 0 -1px 0 0 var(--keyline);
    border-bottom: 0;
}

html[data-theme="light"] .detail-wrapper .ro-sum.due {
    color: var(--ink);
    font-weight: var(--fw-strong);
}

/* ── 11. THE OVERLAYS THAT TELEPORT ────────────────────────────────────────────────────────────
   Finish, cancel, check-in, found-work, the equipment picker and the photo lightbox all render to
   <body>, so none of them can be reached from `.detail-wrapper`. Scoped to their own roots.

   Two faults are shared by every one of them and both are the same faults as the pages: a nested
   filled box inside a dialog that is already a surface, and uppercase tracked labels on a form. */
/* `.mdl-confirm-box` REMOVED from this group. It is not a nested filled box inside a dialog — it is
   a dialog, floating over its own scrim inside the sheet, and this rule was giving it
   `--surface-inset` with no border and no shadow: a grey slab with no boundary on a white sheet.
   `theme-light/_base.css` §C0.5 now owns it and gives it the same white-plus-elevation-3 treatment
   as `.mdl-sheet`. Left here as a note because this file loads AFTER _base.css, so re-adding the
   selector would silently take ownership back.

   DEAD BY SCOPE, 2026-08-11 — `.fwm-sheet` DOES NOT EXIST. `FoundWorkModal.vue` renders
   `<div class="mdl-sheet is-mid">`; every other class in that file is `fwm-*` but the sheet itself
   is not. So `.fwm-sheet .fwm-sec` here, `.fwm-sheet .fwm-eyebrow/.fwm-timenote` below, and
   `.fwm-sheet .fwm-eyebrow > span` at the end of this file have never painted anything. Left in
   place rather than deleted because they are intended decisions, not debris: the fix is to drop the
   `.fwm-sheet ` prefix (the `fwm-*` names are unique), but that is a live paint change on the
   signed-off Work Queue and needs its own verified pass. Do not "fix" it blind. */
html[data-theme="light"] .eqp-panel .eqp-warn,
html[data-theme="light"] .ckm-sheet .ckm-zone,
html[data-theme="light"] .fwm-sheet .fwm-sec {
    background: var(--surface-inset);
    background-image: none;
    border: 0;
    border-radius: var(--r-control);
    box-shadow: none;
}

html[data-theme="light"] .eqp-panel .eqp-item,
html[data-theme="light"] .ckm-sheet .ckm-result {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: inset 0 -1px 0 0 var(--keyline);
}

html[data-theme="light"] .eqp-panel .eqp-item:hover,
html[data-theme="light"] .ckm-sheet .ckm-result:hover {
    background: var(--surface-inset);
    box-shadow: inset 0 -1px 0 0 var(--keyline);
}

/* Selection is tint PLUS a leading bar, never tint alone — in light a tint reads weaker than dark's
   unselected state, so one mechanism is not enough to say "this one". */
html[data-theme="light"] .eqp-panel .eqp-item.on {
    background: var(--surface-inset);
    box-shadow: inset 3px 0 0 0 var(--accent), inset 0 -1px 0 0 var(--keyline);
}

html[data-theme="light"] .eqp-panel .eqp-intro,
html[data-theme="light"] .eqp-panel .eqp-item-reason,
html[data-theme="light"] .eqp-panel .eqp-units,
html[data-theme="light"] .ckm-sheet .ckm-hint,
html[data-theme="light"] .fwm-sheet .fwm-eyebrow,
html[data-theme="light"] .fwm-sheet .fwm-timenote {
    font-size: 0.6875rem;
    font-weight: var(--fw-label);
    letter-spacing: 0;
    text-transform: none;
    color: var(--ink-muted);
}

/* The photo lightbox is the one surface in this theme that SHOULD be dark: it exists to show a
   photograph, and a white frame around an image is a light leak that shifts every colour in it. It
   keeps the scrim as its ground and carries no fill of its own. */
html[data-theme="light"] .modal-panel.photo-lightbox {
    background: none;
    border: 0;
    box-shadow: none;
}

/* Removing `.ro`'s frame also removed the only thing separating it from the quotes beneath — the
   totals row and "Ej godkända offerter" ended up on consecutive lines with nothing between them, so
   the money read as one continuous column. A frame WAS doing separation work here; taking it away
   means the space and the rule have to do that work instead. This is the failure mode of un-carding
   in general: the box is usually holding two jobs at once, and only one of them is decoration. */
html[data-theme="light"] .detail-wrapper .ro {
    margin-bottom: 20px;
}

html[data-theme="light"] .detail-wrapper .quote-section {
    border-top: 1px solid var(--keyline);
    padding-top: 16px;
}

/* `Bekräfta & Avsluta` wrapped onto two lines beside a single-line `Avbryt`, so the two buttons in
   the same footer had different heights and the primary read as the damaged one. The measured button
   padding is tight by design (8px horizontal is much of why a control reads as a control rather than
   as a call to action) and that is fine — what it cannot do is force a two-word Swedish label to
   fold. The label sets the width; the row keeps one height. */
html[data-theme="light"] .modal-panel .modal-actions button,
html[data-theme="light"] .mdl-sheet .mdl-foot button {
    white-space: nowrap;
}
/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   P4 + P6 — MANUAL BOOKING DETAIL (/manual-booking-detail) AND FINISHED JOB DETAIL
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   Append to public/theme-light/detail.css, after the existing P2 block.

   WHAT WAS ALREADY DONE, AND WHY THIS FILE IS SHORT

   `/manual-booking-detail` is not a page. `src/router.ts:32` points it at `./pages/job-detail/`,
   the same component as `/job-detail`; `useJobDetail.ts:60` branches on `isManual` to swap which
   info-blocks render. So P2's whole pass — unframed record, sentence-case eyebrows, the status
   band, the repair order, the neutral Avboka — already landed on it, and screenshots at
   VIEWPORT=wide confirm it: title, identity line, status band, Arbetsorder ledger, Specialutrustning
   chips and the footer button row are all correct in light. Phase 4 is therefore a SHORT LIST of
   what P2 could not see, because P2 was reading a routine visit and an agent booking, and the manual
   record's distinguishing furniture lives in the EDIT SHEET rather than on the page.

   Phase 6, `/finished-job-detail`, shares `.detail-wrapper` with both of them and inherits the same
   furniture. Its one unsolved block is the procedures list.

   Every rule below is prefixed `html[data-theme="light"]` and carries at least two classes, because
   `src/lib/pageStyle.ts` raises every page rule by one class when a page is mounted inside a tablet
   pane (`scopeCss`) — a rule that only just beats the page sheet on desktop loses on tablet.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */


/* ── P6.1. UTFÖRDA ÅTGÄRDER STOPS BEING A STACK OF SUCCESS ALERTS ──────────────────────────────
   Measured: `.procedure-item` is `rgba(4, 120, 87, 0.06)` fill + a `rgba(4, 120, 87, 0.20)` border
   + 12px radius + an inset white top edge, with a green ✓ hung off a `::before`
   (FinishedJobDetailDesktop.css:167-193). Two of them stacked read as two success banners.

   Three separate rules of this theme are broken at once and they all point the same way:

   1. THE VERDICT RULE. This is the list of procedures PERFORMED, on the page for a job that is over.
      Every row is green, always, on every record — a colour that is on every row has stopped
      carrying information. There is no failure case for it to contrast against.
   2. A container has either a border or an elevation, and what is inside it has neither. The record
      is already the surface; a fill AND a border AND a highlight is depth spent three times inside
      something that has already stood down.
   3. The radius ceiling is 4px, and 12px on a full-width row makes a lozenge out of a table row.

   What it becomes is what the Arbetsorder ledger twenty pixels below it already is: rows under an
   eyebrow, separated by an inset hairline. That is deliberate — they are two readings of the same
   visit (what was done, what it bills) and they should read as one column, not as an alert stack
   followed by a table.

   The ✓ stays. It is the list's bullet, and dropping it would leave two bare nouns under a caption
   with nothing marking them as items; it goes to --ink-muted so it is punctuation rather than a
   verdict. CONFIDENT — measured by probe, both themes shot at 1900px. */
html[data-theme="light"] .detail-wrapper .procedure-list {
    gap: 0;                          /* the 0.4rem gap existed to separate boxes; hairlines abut */
}

html[data-theme="light"] .detail-wrapper .procedure-item {
    min-height: var(--row-h-compact);
    padding: 6px 0 6px 22px;         /* 22px = the ✓ column; derived from the row, not the scale */
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: inset 0 -1px 0 0 var(--keyline);
    color: var(--ink);
}

/* The last row drops the divider: a rule under the final row of a group is a rule under nothing,
   and the `.divider` that follows already closes the section. */
html[data-theme="light"] .detail-wrapper .procedure-item:last-child {
    box-shadow: none;
}

html[data-theme="light"] .detail-wrapper .procedure-item::before {
    left: 2px;                       /* the 0.7rem inset was measured off the removed box padding */
    color: var(--ink-muted);
    font-weight: var(--fw-label);
}


/* ── P6.2. TILLÄGGSKOSTNAD — AN EXTRA CHARGE IS NOT A SUCCESS ──────────────────────────────────
   `.cost-block` is the same emerald box as the procedures (FinishedJobDetailDesktop.css: fill 0.07,
   border 0.20, radius 12px, inset highlight) and `.cost-value` prints the figure in emerald ink.
   The block states that the customer is being charged MORE than the quote said. Green for that is
   the notice fault from P2 §5 again, one page over: the one colour a page gets, spent on the
   sentence that least deserves it.

   It goes to the recess treatment — a well inside the record, which is what it structurally is — and
   the figure takes --ink, because a number in a money column is read by its position and its weight,
   not by a hue. The justification quote beside it is already --ink-dim and stays.

   UNVERIFIED: the archived job available in this data set has `added_cost = null`, so this block
   never rendered in any capture. The selectors are read off the sheet and the values are the same
   ones §P6.1 was measured on; the geometry is reasoned, not photographed. */
html[data-theme="light"] .detail-wrapper .cost-block,
html[data-theme="light"] .detail-wrapper .notes-block {
    background: var(--surface-inset);
    border: 0;
    border-radius: var(--r-control);
    box-shadow: none;
}

html[data-theme="light"] .detail-wrapper .cost-value {
    color: var(--ink);
    font-weight: var(--fw-strong);
    letter-spacing: 0;               /* -0.5px is a display-type trick; this is a table figure */
}

/* `.cost-label` is 0.72rem uppercase with 0.6px of tracking — the twelfth eyebrow of this cluster,
   and the `.justification` variant already opts out of both, which is the sheet conceding the point.
   Onto the same rung as every other caption on the page. The `.justification` branch is excluded by
   the second rule so the customer's own words keep their size and their italic. */
html[data-theme="light"] .detail-wrapper .cost-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: 0;
    text-transform: none;
    color: var(--ink-muted);
}
html[data-theme="light"] .detail-wrapper .cost-label.justification {
    font-size: 0.98rem;
    text-transform: none;
    color: var(--ink-dim);
}


/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   P4 — THE MANUAL BOOKING'S EDIT SHEET
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   This sheet is where a hand-booked job is actually maintained, and it is the one surface on the
   route that P2 did not finish, because P2's §9 fixed the field LABELS and the two toggles it could
   see. Four things survived. Scoped to `.modal-panel.booking-modal` — the sheet teleports out of
   `.detail-wrapper` (trap 1), and `.booking-modal` keeps these rules off the other five dialogs that
   also use `.modal-panel`, which belong to other clusters. */

/* ── P4.1. THE DATE FIELD'S LABEL STOPS BEING CENTRED ──────────────────────────────────────────
   "Datum" sits centred over a 260px picker while the other nine labels start at the column's left
   edge, so the form has two left margins. Probed: the label's own `text-align` is already `left` —
   P2 §9 set it and it landed. What centres it is `.date-alone { align-items: center }`, injected at
   runtime from JobDetailDesktop.vue:698, centring the flex COLUMN's children so the label BOX is
   centred and its text-align is irrelevant.

   That is the whole lesson of this rule and it is why it is written down: a text-align fix cannot
   move a box. Stretch the column instead; `.date-alone .dpf` keeps its 260px, so the picker stays
   its own width and simply starts where every other field starts.
   CONFIDENT — cause identified by probe, not by reading. */
html[data-theme="light"] .modal-panel.booking-modal .modal-field.date-alone {
    align-items: stretch;
}

/* ── P4.2. THE MECHANIC PICKER IS A FIELD GROUP, NOT A CARD ────────────────────────────────────
   Probed: `.mech-picker` is `rgba(255,255,255,0.85)` on white, a `rgba(40,81,217,0.22)` border and a
   12px radius (MechanicPicker.vue's scoped block) — a translucent card holding a label and a select,
   inside a dialog that is already the surface. P2 §9 made exactly this call for `.lift-toggle` and
   `.diff-toggle` and this one was missed only because the sheet renders it last.

   The component already has a `.bare` prop for hosts that label the section themselves; this is that
   case in CSS. CONFIDENT — probed before and shot after the reasoning. */
html[data-theme="light"] .modal-panel.booking-modal .mech-picker {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
}

/* The two group captions left inside those unframed groups are 13px/600 while every other label in
   the same column is 11px/500. Same rung, or the form has two kinds of label. */
html[data-theme="light"] .modal-panel.booking-modal .mech-picker-label,
html[data-theme="light"] .modal-panel.booking-modal .diff-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: 0;
    text-transform: none;
    color: var(--ink-muted);
}

/* ── P4.3. THE LIFT SWITCH GOES OFF TEAL ───────────────────────────────────────────────────────
   Probed on: label `rgb(0,118,107)`, track `rgba(13,148,136,0.5)` — LiftToggle.vue's `.on` state
   painting the raw `--ch-teal-2dd4bf` channel. Teal is not in this theme's vocabulary at all; it is
   a dark-theme "success" tint, and "this job needs a lift" is not a success, it is a fact about the
   booking. In light there is exactly one colour that means "on", and it is --accent.

   Only the SWITCH takes the accent. The label goes to --ink like every other field's value, because
   a toggle that is on should not also recolour its own name — the knob's position is the state.

   The 999px track survives the radius ceiling on purpose: --r-pill is allowed for a true track, and
   a switch is the canonical one. CONFIDENT — probed both states' computed values.

   Specificity note: `.lift-toggle.on` compiles to `.lift-toggle.on[data-v-…]` = (0,3,0) from the
   scoped SFC (trap 4), which TIES P2 §9's `.modal-panel .lift-toggle` and is later in document
   order. The `.modal-field` in the chain here is not decoration — it takes this to (0,5,1) so the
   tie cannot go the wrong way. */
html[data-theme="light"] .modal-panel.booking-modal .modal-field .lift-toggle.on {
    background: none;
    border: 0;
    color: var(--ink);
}

html[data-theme="light"] .modal-panel.booking-modal .modal-field .lift-switch {
    background: var(--surface-inset);
    border-color: var(--keyline-strong);
}

html[data-theme="light"] .modal-panel.booking-modal .modal-field .lift-toggle.on .lift-switch {
    background: var(--accent);
    border-color: var(--accent);
}

/* ── P4.4. THE DIFFICULTY SEGMENTS GO OFF THE RAW CHANNELS ONTO THE DECLARED RAMP ───────────────
   Probed: the selected segment is `rgba(184,124,5,0.15)` fill with `rgb(184,124,5)` ink — mustard,
   which is what a darkened amber always is, and the reason this theme has no amber fill. The three
   `.diff-seg.<tier>.on` rules in DifficultyToggle.vue paint the dark ramp's raw teal / amber / rose
   channels directly, so they never consulted a token and light never got a say.

   It does not need a new palette: app.css:2456-2464 declares `--diff-1/2/3` (+ `-soft`, `-line`) in
   the LIGHT block specifically, as a neutral slate→near-black ladder chosen so the tier ramp does
   not collide with the brand. Pointing the segments at it kills the mustard, keeps the ladder
   legible as a ladder (the selected tier gets DARKER as the job gets harder, which is the meaning),
   and makes the segmented control agree with the `.diff-badge` on the page behind it, which already
   renders from those tokens (probed: `rgb(71,85,105)` = --diff-2).

   The unselected segments take the measured control edge — no border, the 1px navy spread, and the
   `0 0 0 0 transparent` placeholder so the hover interpolates instead of snapping. Hover darkens to
   --surface-inset, because on a white sheet there is no headroom above white and a hover that
   resolves to --field-bg-hover (which is literally `#fff` in light) paints nothing.
   CONFIDENT on the token swap and the radius; the hover is reasoned from the rulebook, not shot. */
html[data-theme="light"] .modal-panel.booking-modal .diff-seg {
    border: 0;
    border-radius: var(--r-control);
    background: var(--surface);
    color: var(--ink-dim);
    box-shadow:
        0 0 0 1px rgba(var(--edge-rgb), 0.10),
        0 1px 1px rgba(0, 0, 0, 0.08),
        0 0 0 0 transparent;
    transition: box-shadow 0.08s ease-in, color 0.08s ease-in;
}

html[data-theme="light"] .modal-panel.booking-modal .diff-seg:hover {
    background: var(--surface-inset);
    color: var(--ink);
    box-shadow:
        0 0 0 1px rgba(var(--edge-rgb), 0.10),
        0 1px 1px rgba(0, 0, 0, 0.10),
        0 3px 9px rgba(var(--edge-rgb), 0.10);
}

/* The chosen tier. Fill + a 1px ring in the tier's own line token, so selection is carried twice —
   a tint alone is weaker in light than dark's UNSELECTED state, which is the trap that made every
   first-pass light selection unreadable. */
html[data-theme="light"] .modal-panel.booking-modal .diff-seg.t1.on {
    background: var(--diff-1-soft);
    color: var(--diff-1);
    box-shadow: 0 0 0 1px var(--diff-1-line);
}
html[data-theme="light"] .modal-panel.booking-modal .diff-seg.t2.on {
    background: var(--diff-2-soft);
    color: var(--diff-2);
    box-shadow: 0 0 0 1px var(--diff-2-line);
}
html[data-theme="light"] .modal-panel.booking-modal .diff-seg.t3.on {
    background: var(--diff-3-soft);
    color: var(--diff-3);
    box-shadow: 0 0 0 1px var(--diff-3-line);
}

/* The under-skilled / already-busy disclaimer under the picker. Raw `--ch-orange-fbbf24` at 0.12
   fill, 0.40 border and full-strength ink (MechanicPicker.vue's scoped block) — the same raw amber,
   and this one IS a warning, so it keeps a hue and simply takes the measured one. Severity in light
   is a wash at near-zero chroma with no border and no bar, so the border goes and the fill is the
   status token's own soft rung.

   UNVERIFIED: this only renders when the picked mechanic is under-skilled for the job or
   double-booked, and the seeded record's mechanic is Expert on an Advanced job. Selectors read off
   the component; values are the same --st-expected trio detail.css §D2/§D3 already use. */
html[data-theme="light"] .modal-panel.booking-modal .mech-warn {
    background: var(--st-expected-soft);
    border: 0;
    border-radius: var(--r-control);
    color: var(--st-expected);
    box-shadow: none;
}


/* ── P4.5. "HITTAT MER ARBETE" — AN EYEBROW RULE THAT REACHES NOTHING ──────────────────────────
   The found-work sheet opens from this page's status band, and its three section heads still render
   VAD DU HITTADE / VAD SOM BEHÖVS / SVÅRIGHETSGRAD in uppercase at 0.1em of tracking.

   detail.css:1381 already tries to fix this — `html[data-theme="light"] .fwm-sheet .fwm-eyebrow`
   with `text-transform: none`. It parses, it matches, and it changes nothing, because the uppercase
   is not on `.fwm-eyebrow`: it is on `.fwm-eyebrow > span` (FoundWorkModal.vue:205), the child, and
   a parent's text-transform cannot undo a child's. The existing rule is aimed one element too high.

   This is the same shape of failure as the P2 §8 note about capitalised strings — the declaration is
   correct and lands on the wrong node — and it is worth writing down as a separate species: check
   which ELEMENT carries the transform, not just whether a rule applied.

   The three strings are sentence case in the table (`workqueue_findings_label` = 'Vad du hittade',
   localization.ts:5096/5102, `difficulty_label` = 'Svårighetsgrad', :112), so plain
   `text-transform: none` is enough — no lowercase/::first-letter dance needed here.
   CONFIDENT on the cause; the fix is one selector deeper than a rule already shipped.

   FOR THE LEAD: `.fwm-sheet` is ALSO the Work Queue's found-work sheet, which is on a signed-off
   page. This changes it there too. It is a correction of an override the lead already wrote, not a
   new opinion — but it is a cross-page effect and should be a deliberate call. */
html[data-theme="light"] .fwm-sheet .fwm-eyebrow > span {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: 0;
    text-transform: none;
    color: var(--ink-muted);
}


/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   TOUCH PASS — DETAIL CLUSTER          (phases 15 + 16, author: detail)
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   Two destinations, clearly separated below:

       SECTION A  ->  public/theme-light/detail.css   (append at the end)
       SECTION B  ->  public/theme-light/phone.css    (append at the end — LEAD'S FILE)

   ── WHAT THIS CLUSTER TURNED OUT TO BE ────────────────────────────────────────────────────────
   Verified against the running app rather than assumed, because the brief's page list and the real
   roots disagreed in two places:

     /job-detail             .detail-wrapper   JobDetailDesktop.vue:2            desktop + TABLET
     /manual-booking-detail  .detail-wrapper   router.ts:32 -> the SAME component (isManual flag)
     /finished-job-detail    .detail-wrapper   FinishedJobDetailDesktop.vue:2    desktop + TABLET
     /job-detail   (phone)   .jdp-wrap         JobDetailPhone.vue:2
     /finished-job-detail (phone) .fjd-phone   FinishedJobDetailPhone.vue:2
     /quote-drafts (desktop) .deck-wrapper     QuoteDraftsDesktop.vue:6
     /quote-drafts (TABLET)  .qdt-page         QuoteDraftsTablet.vue:11   <-- NOT .deck-wrapper
     /quote-drafts (phone)   .qdp-wrapper      QuoteDraftsPhone.vue:2
     /quote-draft-detail     .qdd-page         QuoteDraftDetailDesktop.vue:13
     /quote-draft-detail (phone) .qdp-wrapper  QuoteDraftDetailPhone.vue:5

   `.qdt-page` is the finding: `grep -c qdt- public/theme-light/*.css` returns 0 in all eight files.
   The tablet incoming-quotes console has NO light rules anywhere in the theme. See the report.

   ── THE ONE MECHANISM FAILURE THAT EXPLAINS MOST OF THIS FILE ─────────────────────────────────
   `_base.css` §"THE CONTROL FLOOR" writes `html[data-theme="light"] button { min-height: 44px }`
   inside `@media (pointer: coarse)`. That selector is (0,1,2) — one attribute, two elements. Every
   button in this app that carries a themed class is matched by `html[data-theme="light"] .btn-astra-
   neutral`, which is (0,2,1) and WINS. Measured on the tablet, light, with the touch auditor:

       .btn-astra-neutral   36x101   "← Tillbaka"          (detail-wrapper header)
       .btn-astra-glass     36x160   "Redigera"            (detail-wrapper footer)
       .btn-astra-glass     36x300   "Checka in" x3        (phone, .jdp-full-btn)
       .btn-cancel-quiet    36x79    "Avboka"              (detail.css §6 declares the 36 itself)
       .ssel-btn            38x260   "Ingen specifik mekaniker"
       .btn-astra-glass     36x121   "Konversation"        (tablet /quote-drafts record dialog)
       .btn-astra-glass     36x303   "Redigera"            (phone /quote-draft-detail)

   So the blanket floor reaches almost nothing that is actually styled. The primary button DOES get
   its 56 — because _base copies the `:not()` chain exactly, which is the trick that has to be
   repeated for every class-styled control. FOR THE LEAD: the right fix is central — add the themed
   button classes to the coarse floor's selector list in `_base.css`. Section A1 fixes it inside this
   cluster only, so nothing here depends on that happening; delete A1 if the central fix lands.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */


/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   SECTION A  —  public/theme-light/detail.css
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */


/* ── A1. THE CONTROL FLOOR, RE-STATED WHERE IT CAN WIN ─────────────────────────────────────────
   `@media (pointer: coarse)` rather than `.page[data-shell]`, because half of these controls live in
   dialogs that teleport to <body> (the edit sheet, the cancel confirm, the equipment picker, the
   record dialog) and have no `.page` ancestor at all.

   `min-height` only. No `min-width`: _base measured what a blanket width floor did to a compact grid
   and cut it, and nothing in this cluster fails on width for a reason a floor could fix — the four
   that do are square icon buttons, handled one at a time in A4-A6 with a real layout decision each.

   CONFIRMED: the 36/38px measurements above are the auditor's, on the real record at both shells.
   CONFIRMED: rendered with these rules injected — see the report's verification note. */
@media (pointer: coarse) {
    html[data-theme="light"] .detail-wrapper .btn-astra-neutral,
    html[data-theme="light"] .detail-wrapper .btn-astra-glass,
    html[data-theme="light"] .detail-wrapper .btn-cancel-quiet,
    html[data-theme="light"] .detail-wrapper .ssel-btn,
    html[data-theme="light"] .deck-wrapper .btn-astra-neutral,
    html[data-theme="light"] .deck-wrapper .btn-astra-glass,
    html[data-theme="light"] .qdd-page .btn-astra-neutral,
    html[data-theme="light"] .qdd-page .btn-astra-glass,
    html[data-theme="light"] .qdt-page .btn-astra-neutral,
    html[data-theme="light"] .qdt-page .btn-astra-glass,
    /* The teleported dialogs. `.modal-panel` and `.mdl-sheet` are shared with other clusters, which
       is why every other rule in this file is anchored on a page root — but a CONTROL FLOOR is the
       one decision that is identical for every cluster, so there is nothing here to collide over.
       If a second author writes the same rule the declarations are byte-identical. */
    html[data-theme="light"] .modal-panel .btn-astra-neutral,
    html[data-theme="light"] .modal-panel .btn-astra-glass,
    html[data-theme="light"] .modal-panel .ssel-btn,
    html[data-theme="light"] .mdl-sheet .btn-astra-neutral,
    html[data-theme="light"] .mdl-sheet .btn-astra-glass {
        min-height: var(--tap-min);
    }
}


/* ── A2. THE IDENTITY LINE'S TWO LINKS ARE 18px AND 20px TALL ──────────────────────────────────
   Measured on the tablet record: `.ident-cust.link-value` 20x97 ("Mio Håkansson", role=link, opens
   the customer) and `.ident-key.link-value` 18x180 ("Kund-ID CUS-…", role=link, same destination).
   Two of the three navigations available above the fold, and both are a line of text with no box.

   PRE-EXISTING IN BOTH THEMES — dark measures 23x105 and 18x163 on the same two elements, so this is
   not something light introduced. Fixing it in light alone is legitimate (light is a different
   design) and it does move the two themes apart on this line; flagged in the report as a call.

   The floor comes from `min-height` + centring rather than from padding, because `.ident-key` is a
   `justify-self`-ish shrink-to-fit block (`align-self: flex-start`) and `.ident-cust` is a flex item
   in `.ident-line` — in both cases padding would also grow the WIDTH, and the width is already fine.
   `display: inline-flex` keeps `.ident-cust` sitting in its sentence between the two `·` separators.

   Only the `.link-value` variant is touched. A record with no CRM customer renders the same two
   elements without the class and without a click, and a 44px tall non-target is just wasted line.
   CONFIRMED: measured before, rendered after (injected). */
@media (pointer: coarse) {
    html[data-theme="light"] .detail-wrapper .ident-cust.link-value {
        display: inline-flex;
        align-items: center;
        min-height: var(--tap-min);
    }

    html[data-theme="light"] .detail-wrapper .ident-key.link-value {
        display: flex;
        align-items: center;
        min-height: var(--tap-min);
    }
}


/* ── A3. THE CHAT DRAWER — AND THE FACT THAT THIS CLUSTER HAS TWO OF THEM ──────────────────────
   Worth writing down, because a rule aimed at the wrong one parses and does nothing:

     /job-detail + /manual-booking-detail  ->  the page's OWN legacy markup, `.chat-drawer` with
        `.chat-thread-pill` / `.chat-svc-item` (JobDetailDesktop.vue:604, :613)
     /finished-job-detail                  ->  the shared component, `.ctb-*` (ChatThreadBar.vue)

   That REFUTES the report that `.chat-thread-pill` / `.chat-svc-item` are dead selectors: they are
   live on /job-detail's own drawer and on `QuoteChatPanel.vue:31`. What is true is the narrower
   claim ChatThreadBar's own comment makes — the legacy names must not reach THAT component, and
   they do not. registers.css §S3's rules are not dead; they are simply not this page's drawer.

   `.chat-close` measures 44x32 on the tablet: the ONE control that dismisses a full-height drawer,
   32px wide. It is the app's `×` idiom, so it gets a square target rather than a wider box —
   width and height both at the floor, and the glyph stays centred by the flex it already has. */
@media (pointer: coarse) {
    html[data-theme="light"] .chat-close,
    html[data-theme="light"] .mdl-x,
    html[data-theme="light"] .qdt-x {
        min-width: var(--tap-min);
        min-height: var(--tap-min);
    }
}

/* `.ctb-threadnav` is 25.6px wide on BOTH shells (auditor reports 58x26; the 58 is its height, the
   26 is what fails) — CONFIRMING the registers author's measurement. It is the `‹`/`›` pair either
   side of ChatThreadBar's vehicle rail.

   It is NOT widened to 44, and that is the finding rather than a shortcut. On a 412px phone two 44px
   arrows would take 88px — 21% of the viewport — from a rail whose second pill is ALREADY clipped
   (measured: `.ctb-threads` is 361px wide and holds 961px of pills). And the arrows are not the only
   way to reach them: `.ctb-threads` is `overflow-x: auto` (ChatThreadBar.vue:169), so on a touch
   shell the rail is swiped. A scroll-nudge button is a mouse affordance; on a finger it is a
   25px-wide substitute for the gesture that already works.

   So they are removed on coarse pointers and the 52px goes back to the pills. This is the same shape
   of decision _base names for the mini-calendar arrows — a layout call on a compact grid, made where
   the grid is owned — and it is the cheap direction of that call rather than the expensive one.
   CONFIRMED (rendered, injected): with the arrows gone `.ctb-threads` measures 412x58 at x=0 instead
   of 361x58 at x=26 — the rail takes the whole viewport and the second pill is no longer clipped by
   the right arrow. The `overflow-x: auto` that makes it swipeable is the component's own and is
   untouched; that half is read off ChatThreadBar.vue:169 rather than dragged. */
@media (pointer: coarse) {
    html[data-theme="light"] .ctb-threadnav {
        display: none;
    }
}

/* The vehicle and service pills are 44px tall already (measured 44 on phone) and need no floor. What
   they do need is the radius: they are `border-radius: 999px` (ChatThreadBar.vue:191), which SWALLOWS
   the `inset 3px 0 0 var(--accent-bar)` selection bar detail.css §"ChatThreadBar — the orphan"
   already gives them. Rendered, the active pill reads as a violet-ringed capsule and the leading bar
   is not visible at all — i.e. selection is carried by tint alone, the one thing the rulebook says is
   not enough in light.

   Scoped to coarse ONLY, deliberately: the desktop light design for this bar has been looked at and
   signed off at 4px-swallowing 999px, and re-cutting it there is a colour-pass decision, not a touch
   one. FOR THE LEAD: the same argument applies at desktop and probably should be taken there too.
   CONFIRMED: bar invisible before, visible after (rendered, injected). */
@media (pointer: coarse) {
    html[data-theme="light"] .ctb-thread-pill,
    html[data-theme="light"] .ctb-service-pill {
        border-radius: var(--r-control);
    }
}


/* ── A4. THE TIME PICKER'S CLOCK BUTTON, IN THE BOOKING SHEET ──────────────────────────────────
   Measured in the edit sheet on the tablet: `.tpf-ic-btn` is 30x44 — right height, and 30px wide,
   sitting inside a 149px field in a two-up row. It is the only way to open the time list without
   typing, on the sheet where a bay tablet actually re-times a job.

   14px is taken from the text input beside it, not from the field: the row is `Starttid` / `Sluttid`
   at 149px each inside a 310px column, and widening the field would push the pair out of the sheet.
   A time string is five characters; 14px of a 119px input is not a truncation risk.

   Scoped to `.modal-panel.booking-modal` for the same reason detail.css §P4 is: this sheet teleports
   out of `.detail-wrapper`, and `.booking-modal` keeps the rule off the five other dialogs that
   share `.modal-panel` and belong to other clusters. CONFIRMED (measured + rendered). */
@media (pointer: coarse) {
    html[data-theme="light"] .modal-panel.booking-modal .tpf-ic-btn {
        min-width: var(--tap-min);
    }
}


/* ── A5. THE DESTRUCTIVE PAIR GETS ITS AIR ─────────────────────────────────────────────────────
   `--tap-sep` exists for exactly one situation and this is it. Measured on the tablet cancel dialog:
   "Behåll bokning" ends at x=528 and "Avboka" starts at x=537 — a 9px gap between a 56px keep button
   and a 56px irreversible one. It clears the 8px minimum by a pixel, which is not the same as being
   separated; with a glove's contact patch the dominant error is the adjacent-mistap, and 9px is
   inside one.

   `margin-left` ADDS to the flex gap that is already there rather than replacing it (17px total),
   which is why this is written as a margin and not as a gap on the row: the row's gap is also the
   spacing for the non-destructive pairs, and those are correct as they are.

   Applied by the danger class, so it lands only where the pair is genuinely confirm-next-to-destroy.
   CONFIRMED (measured before; rendered after). */
@media (pointer: coarse) {
    html[data-theme="light"] .modal-actions .btn-astra-danger,
    html[data-theme="light"] .mdl-foot .btn-astra-danger,
    html[data-theme="light"] .qdt-foot .btn-astra-danger {
        margin-left: var(--tap-sep);
    }
}


/* ── A6. THE EQUIPMENT PICKER IS ALREADY RIGHT, AND THAT IS WORTH RECORDING ────────────────────
   No rule. `_base.css` lists "the label target is per-cluster work, listed in the step-2 brief,
   where the real class name is known" for checkbox rows, and this cluster's checkbox row is the
   equipment picker. Measured: the auditor reports two bare `input` at 18x18, which is the checkbox
   glyph and is deliberately excluded from the floor — and `.eqp-item`, the `<label>` that WRAPS it
   (EquipmentPickerModal.vue:13), measures 406x62 on the tablet. The target already exists and is
   already 18px above the floor.

   Recorded rather than silently skipped, because "checkbox under the floor" is exactly the shape of
   an audit line that gets fixed twice. And `:has()` is not needed here at all — the label carries a
   class. */


/* ── A7. THE 💬 IS DRAWN BY THE OPERATING SYSTEM ───────────────────────────────────────────────
   `ReachButton.vue:9/12/15` renders the literal characters 💬 and 📞 as the button's only content.
   U+1F4AC and U+1F4DE are emoji-presentation codepoints, so Windows and Android draw them in full
   colour from the system emoji font and macOS draws a different silhouette. Rendered on the tablet
   it is a pale lavender blob on a white disc — the one control that opens the customer conversation,
   drawn by the platform rather than by this design system.

   Only the TARGET is fixed here (measured 44x36 — the height is fine, the width is not; a round
   button must be square to be round). The GLYPH needs `--icon-chat` and `--icon-phone`, and _base's
   own recipe says to ask for the token rather than inline a second data URI:

       ESCALATED TO THE LEAD — app.css needs --icon-chat and --icon-phone beside --icon-lock and
       --icon-pencil. With them, the fix is the same suppress-and-replace detail.css §8.2 already
       uses for `.rsum-pen`, and it belongs in _base because `.chat-pill` renders on eight pages
       across four clusters (job detail, finished job detail, quote history, quote drafts phone,
       vehicle detail, customer detail, …).

   CONFIRMED (measured); the width fix rendered. */
@media (pointer: coarse) {
    html[data-theme="light"] .chat-pill {
        min-width: var(--tap-min);
    }
}


/* ── A8. /quote-drafts ON THE TABLET — A BESPOKE VIEW WITH NO LIGHT RULES AT ALL ───────────────
   `.qdt-page` (QuoteDraftsTablet.vue:11) is a real tablet view, not the desktop narrowed, and the
   theme has zero rules for it: `grep -c 'qdt-' public/theme-light/*.css` is 0 in every file. Its
   TOUCH TARGETS audit clean at 1080x810 (26 elements, all clear; the record dialog adds one 36px
   `.btn-astra-glass`, fixed by A1), so nothing below is a touch fix — these are the two structural
   faults that are visible in the same screenshot and cost nothing to state:

   1. The ledger has no surface. `_base.css` §"SCROLL REGIONS NEED AN EDGE" is explicit that a list
      region on the canvas needs `--surface` + a `--keyline`, and the rows here sit directly on
      `#f6f8fa` with no card and no dividers. This is the light theme's silent failure mode: nothing
      throws, the region is simply not drawn.
   2. Rows have no rule between them, so two quotes read as one paragraph.

   A full light pass on `.qdt-*` is a colour/structure job and is NOT in this brief. What is written
   here is the minimum that stops the list from being invisible; the rest is in the report as a gap.
   CONFIRMED (rendered, injected): `.qdt-list` comes back at 910x118 with `--r-card` and a keyline,
   and the row measures 908x72 — above the tablet's 64px gloved rung, so the min-height below is a
   floor rather than a change. The COLOUR half of this view (an amber "1 nytt svar" capsule, capsule
   chips, an amber "N behöver dig") is deliberately NOT touched: that is a colour pass on a view with
   no light rules at all, and it is in the report as a gap rather than half-done here. */
html[data-theme="light"] .qdt-page .qdt-list {
    background: var(--surface);
    border: 1px solid var(--keyline);
    border-radius: var(--r-card);
    overflow: hidden;
}

html[data-theme="light"] .qdt-page .qdt-row {
    border-radius: 0;
    box-shadow: inset 0 -1px 0 0 var(--keyline);
}

html[data-theme="light"] .qdt-page .qdt-row:last-child {
    box-shadow: none;
}

/* The row is a `<button>`, so on a coarse pointer it is already at the 44px floor from _base — but
   the tablet's density ladder resolves `--row-h` to 64px (the gloved ISA-101 rung) and a ledger row
   on the bay tablet should consume it rather than sit at the universal minimum. Derived, not
   authored: the rule states the height and lets the existing padding centre inside it. */
@media (pointer: coarse) {
    html[data-theme="light"] .page[data-shell='tablet'] .qdt-page .qdt-row {
        min-height: var(--row-h);
    }
}


/* ═══════════════════════════════════════════════════════════════════════════════════════════════


/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   INCOMING QUOTES — THE PAINT HALF OF THE WIDTH + HARMONIZATION PASS
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   The geometry half of this pass lives unprefixed in the page sheets (QuoteDraftsDesktop.css's rail
   reservation, QuoteDraftDetailDesktop.css's --lx-page measure and the blocker's position in the
   foot). Nothing below moves a width.

   "LOTS OF GREY FIELDS", COUNTED. Probed on /quote-draft-detail in light at 1900 — computed
   background-color, not read off CSS:

       body                #f6f8fa   canvas
       .qdd-sheet          #ffffff   the outermost surface
       .qdd-head           #dcdee6   ← grey 1   a band across the top of the sheet
       .qdd-zone   ×3      #f3f6f8   ← grey 2   a panel per column
       .qdd-group-head ×3  rgba(27,35,61,.043)  ← grey 3   a band per group, INSIDE grey 2
       .qdd-totals         #d5d8e0   ← grey 4   a slab, INSIDE grey 2, darker than everything
       .qdd-foot           #d5d8e0   ← grey 5   a band across the bottom
       .qdd-msg            #ffffff + border + elevation-1  ← a RAISED WHITE CARD inside grey 2

   Down the middle column that is white sheet → grey zone → darker grey band → darker grey slab:
   four surfaces to say "this is a number in a group in a form". Depth is spent ONCE, at the
   outermost surface. And `.qdd-msg` is the tell that this was never a colour decision — the letter
   was raised to be "the lightest thing on the document", which it only needed because its
   surroundings had gone grey. Every one of the five greys is a workaround for the one above it.

   What replaces them is Telefon's answer (boards.css §"TELEFON: THE RECORD, FINISHED"): the
   document is plain white with air around it because it is already inside a frame, and a summary
   recedes onto one top rule.

   THE DESKTOP CONSOLE, /quote-drafts, IS NOT PART OF THIS. `.qd-offer`, `.qd-agreed`,
   `.qd-group-head`, `.qd-totals`, `.qd-pay`, `.qd-actions` all probe with NO background-color at
   all — transparent, structured by rules. That pane is already correct; it takes one radius and the
   money colour below and nothing else. */


/* ── P1. THE EDITOR: SPEND DEPTH ONCE ──────────────────────────────────────────────────────────
   P1a — THE SHEET IS THE ONE SURFACE. Stated so the border is a measured keyline rather than
   --glass-border, and so no elevation creeps back in. White on the #f6f8fa canvas with a hairline
   is this theme's standard page card — how Work Queue and Telefon are drawn. */
html[data-theme="light"] .qdd-page .qdd-sheet {
    background: var(--surface);
    border: 1px solid var(--keyline);
    box-shadow: none;
}

/* P1b — THE HEAD AND THE FOOT STOP BEING BANDS. #dcdee6 and #d5d8e0 are the two darkest fills on
   the page and they are spent on the frame's furniture. A dark floor under a dialog is a DARK-theme
   device: it works because it is a well below a lit panel. In light there is nothing below white, so
   --well-a22 renders as exactly what the user called it — a grey field. A pinned foot needs to say
   "the form ends here", and a rule says that. */
html[data-theme="light"] .qdd-page .qdd-head {
    background: var(--surface);
    border-bottom: 1px solid var(--keyline);
}

html[data-theme="light"] .qdd-page .qdd-foot {
    background: var(--surface);
    border-top: 1px solid var(--keyline);
}

/* P1c — THE THREE ZONE PANELS GO. The zones exist because of a DARK problem the page sheet states
   itself (QuoteDraftDetailDesktop.css — "three panels, so the three columns read as three things").
   True in dark, where a 9%-white hairline is invisible and a fill is the only separator available.
   Not true in light: --keyline on white is a line you can see, and every zone already OWNS one —
   `.qdd-zone-head` carries a border-bottom, so each column is already underlined at the same y.
   Three eyebrows, three rules, three columns, a --sp-6 gutter. The padding goes WITH the background
   or the columns keep their old footprint and read as invisible boxes.
   Deliberately NOT done: a vertical divider between the columns — that is a table gridline. */
html[data-theme="light"] .qdd-page .qdd-zone {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
}

html[data-theme="light"] .qdd-page .qdd-zone-head {
    border-bottom: 1px solid var(--keyline);
}

/* P1d — THE LEDGER BAND HEADERS. "ARBETE & PRIS", "DELAR", "AVGIFTER" — three filled, bordered,
   rounded bars, one per group, inside a zone that was itself filled. A group heading in a statement
   is a label and a rule; it is not a container. The base sheet's original declaration is already
   right; the dark pass boxed it. This restores the base shape and takes the rule to
   --keyline-strong, because a group boundary out-ranks the --keyline under each line inside it.
   Side padding to 0 so the heading's left edge sits on the same vertical as the names beneath it. */
html[data-theme="light"] .qdd-page .qdd-group-head {
    background: none;
    border: 0;
    border-bottom: 1px solid var(--keyline-strong);
    border-radius: 0;
    padding: 0 0 var(--sp-2);
    margin-bottom: 0;
}

/* P1e — THE TOTALS SLAB AND THE LETTER, ONE DECISION. `.qdd-totals` is the darkest object on the
   page and it holds the number the page exists to produce; a summary does not out-weigh the
   document it summarises. Telefon's `.pc-total` is the precedent: transparent, one top keyline. The
   `.qdd-pay` double rule under it is kept — that is the statement convention and the emphasis the
   slab was standing in for. `.qdd-msg` loses the card it was given for being lighter than a grey
   zone that no longer exists; a white card with a shadow on a white sheet is an object drawn twice.
   It keeps its LEADING RULE, the quotation convention, on the same rung as the group rule.
   All three properties are NAMED because the .qdd-msg block above sets all three. */
html[data-theme="light"] .qdd-page .qdd-totals {
    background: none;
    border: 0;
    border-top: 1px solid var(--keyline);
    border-radius: 0;
    box-shadow: none;
    padding: var(--sp-3) 0 0;
    margin-top: var(--sp-4);
}

html[data-theme="light"] .qdd-page .qdd-msg {
    background: none;
    border: 0;
    border-left: 2px solid var(--keyline-strong);
    border-radius: 0;
    box-shadow: none;
    padding: 0 0 0 var(--sp-4);
}

/* P1f — ONE NUMBER, ONE COLOUR. With the slab gone, `.qdd-pay-value` resolves to ink while
   `.qdd-hl-amount` in the headline is still --st-logged green: the same figure printed twice on one
   screen in two colours, which reads as two facts. It was hidden before only because the green one
   sat on white and the ink one sat on a grey slab. A price is neither good nor bad. */
html[data-theme="light"] .qdd-page .qdd-hl-amount { color: var(--ink); }

/* P1g — THE HINT THAT BECAME THE LOUDEST THING IN ZONE 3. `.mdl-hint.warn` is --fin-warn at
   --fw-600, which in light is near-black bold — three lines of it under the field, out-weighing the
   field's own label and every heading in the column. A hint is prose you read, not a headline. It is
   the same required-field state as the blocker, so it takes the same status ink at body weight. */
html[data-theme="light"] .qdd-page .mdl-hint.warn {
    color: var(--st-expected);
    font-weight: var(--fw-body);
}

/* P1h — THE ONE PLACE A FILL IS STILL RIGHT, recorded so it is not "cleaned up" later. A line being
   edited (`.qdd-line.editing`) takes --fin-well: that is not a container fill, it is a STATE on a
   single row, and a state is the one thing a tint is for. Same for the input wells — a control
   carries its own border by right; the house rule bans a border on a CONTAINER inside a card. */


/* ── P2. THE EXCLAMATION MARK ──────────────────────────────────────────────────────────────────
   What it is, before changing it: `.qdd-blockers` is a real <button> rendered only when something
   blocks the send, whose click scrolls to the first unanswered required field. The bare `!` is not
   in the markup — it is the `.qdd-blockers::before` above, added when the bare amber dot was
   correctly hidden. The SIGNAL is load-bearing and stays: it is the only thing on the page that says
   why the filled "Bekräfta & Skicka" will not send. What was wrong is placement (fixed in the page
   sheet: it now sits beside the button it blocks) and dress.

   THE BANG WAS NAKED. This theme's status idiom is a wash + dark ink + a shape-distinct glyph,
   precisely so a glyph reads as a glyph and not as a stray character. A `!` with no shape around it,
   at the far corner of a wide bar, is indistinguishable from a typo — which is how the user
   described it: "some exclamation mark bottom left". They did not read it as a warning. And once
   P1b turns the foot white it would be loose amber text floating on nothing.

   A 5% wash of the hue, no border, no left bar. --r-control, a rectangle rather than a capsule:
   this is not a control-shaped object even though it is clickable — it is a sentence you can press.
   Hover goes DARKER, per the light rulebook; the existing `:hover { color: --fin-ink }` on a white
   ground painted almost nothing. `.qdd-ready` — the same slot when nothing blocks — goes NEUTRAL,
   not green: "Klar att skicka" is true of every quote the moment before it is sent, so it carries no
   information and the hue stays reserved for the case that is actually wrong. */
html[data-theme="light"] .qdd-page .qdd-blockers {
    padding: 6px var(--sp-3);
    border: 0;
    border-radius: var(--r-control);
    background: var(--st-expected-soft);
    color: var(--st-expected);
    font-weight: var(--fw-label);
}

html[data-theme="light"] .qdd-page .qdd-blockers:hover {
    background: var(--st-expected-line);
    color: var(--st-expected);
}

/* The bang gets the badge's weight and a fixed advance so the sentence does not shift when the count
   goes from 1 to 2. `content` is NOT restated — the block above sets it, and restating it in a
   second block is how one glyph becomes two declarations that drift. */
html[data-theme="light"] .qdd-page .qdd-blockers::before {
    display: inline-block;
    width: 1em;
    text-align: center;
    margin-right: 2px;
    font-weight: var(--fw-strong);
}

html[data-theme="light"] .qdd-page .qdd-ready {
    color: var(--ink-muted);
    font-weight: var(--fw-body);
}


/* ── P3. THE REQUIRED FIELD THE BLOCKER POINTS AT ──────────────────────────────────────────────
   `DifficultyToggle.vue`. It is the field the bang is about, so it is in scope, and in light it
   carries three faults: 12px/9px radii against a 4px ceiling; `.diff-req` on --c-orange-fbbf24, a
   LITERAL amber channel for a status, which resolves warm-brown on white; and on the console its
   three segments stretch to ~590px each on `flex: 1` across a 1212px pane — a button is sized by its
   word, and three 590px segments read as a toolbar.

   SHARED COMPONENT — SCOPED ON PURPOSE. This control also renders in the manual-booking sheet and on
   the mechanic roster. Every rule is anchored on one of the two page roots so nothing leaks;
   `.qdd-page .diff-toggle` is (0,3,1) against the scoped SFC's (0,2,0), so these win. The tag takes
   --st-expected, the same ink as the blocker that points at it: one required field, one colour, two
   places. `justify-content: flex-start` as well as `flex: 0`, or the segments centre in the leftover
   space and the row reads as a centred toolbar instead of a left-aligned choice. */
/* EDITED after the render, see P7: this block used to give `.diff-toggle` a white fill and a
   keyline. That made a bordered container whose only content is three bordered controls — the frame
   §"The lift toggle is a field group wearing the costume of a card" already removes on
   `.modal-panel`. The group yields; the segments below keep their edges. `.qdt-page` is added
   because the tablet renders the same component (QuoteDraftsTablet.vue:168). */
html[data-theme="light"] .qdd-page .diff-toggle,
html[data-theme="light"] .deck-wrapper .diff-toggle,
html[data-theme="light"] .qdt-record .diff-toggle {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
}

html[data-theme="light"] .qdd-page .diff-req,
html[data-theme="light"] .deck-wrapper .diff-req,
html[data-theme="light"] .qdt-record .diff-req { color: var(--st-expected); }

html[data-theme="light"] .qdd-page .diff-segments,
html[data-theme="light"] .deck-wrapper .diff-segments,
html[data-theme="light"] .qdt-record .diff-segments { justify-content: flex-start; }

/* `.qdt-record` is left OUT of the segment sizing below on purpose: `min-width: 7rem` at the
   desktop's 12.8px is a mouse-sized word, and the bay tablet's segments are the one place the
   stretched `flex: 1` is right — three 211px gloved targets in an 670px sheet is what a coarse
   pointer wants. Only the frame and the radius are shared with the console, above. */
html[data-theme="light"] .qdd-page .diff-seg,
html[data-theme="light"] .deck-wrapper .diff-seg {
    flex: 0 0 auto;
    min-width: 7rem;
    padding: 0 var(--sp-4);
    min-height: var(--ctl-h);
    border-radius: var(--r-control);
}


/* ── P4. THE TABLET CONSOLE — .qdt-page ────────────────────────────────────────────────────────
   §A8 above already gave this view its SURFACES (list card, row hairline, coarse-pointer row
   height) and recorded what it left: "the COLOUR half of this view (an amber '1 nytt svar' capsule,
   capsule chips, an amber 'N behöver dig') is deliberately NOT touched". This is that half. Probed
   at 1080×810 in light, before: `.qdt-state` #b87c05 on a 999px pill, `.qdt-row-money` #047857
   green, `.qdt-needs` amber on the result line, `.qdt-pay-value` green.

   P4a — THE STATE CHIP. A capsule means "this is a control" in this theme and this is a label.
   Rectangle, the measured status trio, and the mandatory shape-distinct glyph — `.qdt-state` is the
   ONLY thing on a tablet row that says a quote needs a human, and colour alone collapses under
   deuteranopia. The chip takes a runtime class `r-${kind}` (`r-newer_messages` in the DOM, so the
   dot-to-dash convention does not apply here — verified in the DOM, not guessed); only `.r-ready` is
   named in the page sheet, so the base rule covers every other kind by falling through.
   `.r-ready` goes NEUTRAL: "Klar" is the ordinary case on this board, and green there was competing
   with the amber that actually needs a press. */
html[data-theme="light"] .qdt-page .qdt-state {
    border-radius: 4px;
    padding: 2px 6px;
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    background: var(--st-expected-soft);
    border: 1px solid var(--st-expected-line);
    color: var(--st-expected);
}

html[data-theme="light"] .qdt-page .qdt-state::before {
    content: "!";
    display: inline-block;
    width: 1em;
    margin-right: 2px;
    text-align: center;
}

html[data-theme="light"] .qdt-page .qdt-state.r-ready {
    background: var(--surface-inset);
    border-color: var(--keyline);
    color: var(--ink-muted);
}

html[data-theme="light"] .qdt-page .qdt-state.r-ready::before { content: "•"; }

/* P4b — MONEY GOES TO INK, tabular, as Telefon's `.lx-money` block argues: a price is the fact the
   page exists to produce, and painting it mint says "good" about a number that is neither good nor
   bad. On this board it is green on every row, and it was the only green on the screen, so it read
   as a status the row does not have. */
/* ⚠ SCOPE, RE-DERIVED ON THE RENDER. `.qdt-pay-value` and `.qdt-group-sum` only ever render inside
   the RECORD DIALOG, and that dialog is a SIBLING of `.qdt-page` in the template, not a descendant
   (QuoteDraftsTablet.vue:112 closes `.qdt-page`; :118 opens `.mdl-backdrop`). So `.qdt-page .qdt-*`
   parses cleanly and matches nothing there — the same failure mode as a teleported overlay, from a
   different cause. The dialog's own root class is `.qdt-record` (on `.mdl-body`, :125), which is
   unique to this view; every dialog rule below is anchored on it. Verified by opening the record on
   the tablet and re-scanning: before this, the dialog's document still carried the violet rule and
   its `.diff-toggle` still carried the frame P3 had removed everywhere else. */
html[data-theme="light"] .qdt-page .qdt-row-money,
html[data-theme="light"] .qdt-record .qdt-pay-value,
html[data-theme="light"] .qdt-record .qdt-group-sum {
    color: var(--ink);
    font-weight: var(--fw-label);
    font-variant-numeric: tabular-nums;
}

/* The age is a fact until it is a problem. */
html[data-theme="light"] .qdt-page .qdt-row-age {
    color: var(--ink-muted);
    font-variant-numeric: tabular-nums;
}

/* P4c — THE RESULT LINE. "1 offert · 4 056 kr · 1 behöver dig": a sentence fragment carrying a
   second amber and a second green. The count that needs you keeps the hue — it IS the exception —
   but at label weight in the status ink rather than at the literal channel. NOT boxed: boxing
   Telefon's `.pb-result-urgent` turned prose into a badge. */
html[data-theme="light"] .qdt-page .qdt-needs { color: var(--st-expected); }
html[data-theme="light"] .qdt-page .lx-result-sum { color: var(--ink); font-variant-numeric: tabular-nums; }

/* P4d — THE PLATE. --surface-a07 over an `rgba(--sf-rgb, .11)` border: in light --sf-rgb is the INK
   channel, so this is a grey wash with a grey line — the same "nearly there" panel P1c fixed on
   `.qdd-zone`. It is a noun on a row, so it takes the neutral badge rectangle every other tag takes.
   UNVERIFIED ON THE RENDER — this shop's incoming rows carry no registration, so the tag did not
   appear in the capture. The declaration is copied from `.rt-season` / `.qh-source` rather than
   invented, but nobody has seen it on this page. */
html[data-theme="light"] .qdt-page .qdt-plate {
    border-radius: 4px;
    padding: 1px 6px;
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    color: var(--ink-dim);
}

/* P4e — THE RECORD DIALOG'S DOCUMENT. `.qdt-offer` / `.qdt-agreed` carry the same 2px violet leading
   rule the desktop `.qdd-msg` had, which the light pass already neutralised above without knowing
   the tablet kept its own copy. Two views, one document, one rule. */
html[data-theme="light"] .qdt-record .qdt-offer,
html[data-theme="light"] .qdt-record .qdt-agreed { border-left-color: var(--keyline-strong); }

html[data-theme="light"] .qdt-record .qdt-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
    color: var(--ink-muted);
}

/* P4f — THE PRESS STATE. `rgba(--sf-rgb, .05)` on white is a real if faint grey, so unlike most of
   this theme's hover bugs it is not invisible. Restated on the measured token anyway: an active row
   is the one press this shell is built around, and in light a state goes DARKER. */
html[data-theme="light"] .qdt-page .qdt-row:active { background: var(--surface-active); }

/* P4g — THE EMPTY STATE is left alone deliberately: --ink-dim over --ink-muted is a correct two-step
   ladder for a message that is good news. Recorded so it is not audited twice. */


/* ── P5. THE PHONE VIEW — .qdp-wrapper ─────────────────────────────────────────────────────────
   THE SAME FAULT AS THE EDITOR, ONE SHELL DOWN. Reading down the card: `.qdp-card` (#f2f4f7) →
   `.deck-labour-row` (bordered box) → `.deck-total` (bordered box) → `.full-quote-toggle` (bordered
   box) → `.deck-booked` (capsule) → `.card-catchup` (bordered box with a left bar and an amber dot).
   Six boxes to show one quote on a 412px screen; every one a container in a container.

   ⚠ CROSS-CLUSTER LEAK, RECORDED. This card's totals block uses `.dt-head` / `.dt-rows` / `.dt-row`
   / `.dt-row.grand` (QuoteDraftsPhone.css). `.dt-*` IS ALSO THE DROPOFFS BOARD'S PREFIX and
   `boards.css` carries ~30 UNANCHORED rules on it — one sets `.dt-row`'s border colour and another
   gives `.dt-row-main` a hover. Those already reach this phone card today. Every rule below is
   anchored on `.qdp-wrapper`; the boards.css ones are not, and that outlives this pass.

   P5a0 — THE OUTERMOST SURFACE, settled before anything inside it can be. `.qdp-card` is --surface-1
   with a violet-tinted border and composites to #f2f4f7 — a grey card on a ground that is itself not
   white. Every "white box inside it" that P5a-d deletes was white only relative to this. */
html[data-theme="light"] .qdp-wrapper .qdp-card {
    background: var(--surface);
    border: 1px solid var(--keyline);
    box-shadow: none;
}

/* P5a1 — THE HOURS STEPPER IS A FIELD INSIDE A FIELD. `.dlr-hours` is a bordered, filled box whose
   only content is `.dlr-input`, which declares `background: transparent; border: none` — and then
   _base's light field treatment paints the bare `input` white with its own ring anyway. A white
   bordered box inside a grey bordered box, 44px wide, for one digit. The wrapper yields: the input
   is the thing you tap, so the input keeps the frame. */
html[data-theme="light"] .qdp-wrapper .dlr-hours {
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0;
}

/* P5a — the totals block: a violet-tinted GRADIENT panel with a violet border. A tinted surface is a
   dark-theme device. It is the summary of the card above it, so it takes the summary treatment —
   one top rule. */
html[data-theme="light"] .qdp-wrapper .deck-total {
    background: none;
    background-image: none;
    border: 0;
    border-top: 1px solid var(--keyline);
    border-radius: 0;
    box-shadow: none;
    padding: var(--sp-3) 0 0;
}

html[data-theme="light"] .qdp-wrapper .dt-rows,
html[data-theme="light"] .qdp-wrapper .dt-row.grand { border-top-color: var(--keyline); }

/* ⚠ ONE EXTRA CLASS, AND WHY — measured, not defensive. `public/theme-light/phone.css` is the LAST
   of the eight theme sheets in index.html, and phone.css:250 declares
   `html[data-theme="light"] .qdp-wrapper .deck-total { background: var(--recess-a03) }` at exactly
   the specificity of the block above. Later wins, so the block above landed its padding and its top
   rule (phone.css names neither) and lost its background: probed after applying, `.deck-total` still
   came back rgb(240,241,245) — the grey slab the user is pointing at, one shell down.
   `.qdp-card` is a real ancestor (QuoteDraftsPhone.vue: .qdp-card > .card-quote > .deck-total), so
   this is a true statement about the DOM rather than a specificity trick, and it is (0,4,1).
   THE PROPER FIX IS TO DELETE phone.css:250-255, which this pass supersedes — that file belongs to
   another author, so the collision is recorded here rather than reached into. Same story, lower
   stakes, for `.card-catchup`'s border-left-color (phone.css:241) and the `.dt-rows` rule above
   (phone.css:255): those two lose to phone.css as well, but both are one keyline rung apart from
   what this block asks for, so they are left rather than escalated. */
html[data-theme="light"] .qdp-wrapper .qdp-card .deck-total {
    background: none;
    background-image: none;
    border: 0;
    border-top: 1px solid var(--keyline);
    border-radius: 0;
    box-shadow: none;
}

/* P5b — the line item, the full-quote disclosure and the catch-up block: three bordered boxes inside
   the card, all three now a row with a rule under it. `.card-catchup` keeps its LEADING rule — the
   quotation convention, same as `.qdd-msg` and `.qdt-offer`: one document, three shells, one
   treatment — and loses its fill and its radius. */
html[data-theme="light"] .qdp-wrapper .deck-labour-row,
html[data-theme="light"] .qdp-wrapper .full-quote-toggle {
    background: none;
    border: 0;
    border-bottom: 1px solid var(--keyline);
    border-radius: 0;
    box-shadow: none;
}

html[data-theme="light"] .qdp-wrapper .card-catchup {
    background: none;
    border: 0;
    border-left: 2px solid var(--keyline-strong);
    border-radius: 0;
    box-shadow: none;
}

/* The amber disc with its 3px glow beside "VAD NI KOM ÖVERENS OM" is the exact device _base warns
   cannot carry a status alone — and it is not carrying one: it marks that a summary exists. */
html[data-theme="light"] .qdp-wrapper .catchup-dot {
    background: var(--keyline-strong);
    box-shadow: none;
}

/* P5c — money to ink, one shell down. Same argument as P4b; worse here, because the green figure is
   the largest coloured object on a 412px screen. */
html[data-theme="light"] .qdp-wrapper .dt-value,
html[data-theme="light"] .qdp-wrapper .dt-row.grand { color: var(--ink); }

/* P5d — the "BOKAD TID" capsule. A 999px outlined pill stating a fact; a capsule means "this is a
   control" and this is not one. Rectangle at the badge rung. */
html[data-theme="light"] .qdp-wrapper .deck-booked {
    border-radius: 4px;
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    color: var(--ink-dim);
}

html[data-theme="light"] .qdp-wrapper .deck-booked.flag {
    background: var(--st-expected-soft);
    border-color: var(--st-expected-line);
    color: var(--st-expected);
}


/* ── P6. THE DESKTOP CONSOLE'S REMAINING TWO ───────────────────────────────────────────────────
   `.qd-newer` / `.qd-arrivals` probe white, 1px border, `border-radius: 999px`. They ARE controls,
   so a capsule is defensible by the theme's own shorthand — but 4px is a ceiling, every other button
   on this pane is at --r-control, and one capsule among rectangles reads as a chip that wandered in
   from a filter bar. The rest of their treatment is correct and is not restated. */
html[data-theme="light"] .deck-wrapper .qd-newer,
html[data-theme="light"] .deck-wrapper .qd-arrivals { border-radius: var(--r-control); }

/* P6b — AND THE GREEN. The pane probes transparent and structured by rules, which is the finished
   state for SURFACES — but it still prints four green figures: `.lx-money`, `.lx-bucket-sum`,
   `.lx-result-sum` and `.qd-pay-value`, on a board where every row has a price. This is Telefon's
   `.lx-money` fix applied to the page that shares the component with it (src/lib/listLedgerStyle.ts);
   boards.css scopes that fix to `.pb-wrapper`, so /quote-drafts never got it even though it renders
   the identical markup. Two consumers of one ledger, one of them fixed: that is the shared
   component's fix scoped too narrowly, not a new decision here. */
html[data-theme="light"] .deck-wrapper .lx-money,
html[data-theme="light"] .deck-wrapper .lx-bucket-sum,
html[data-theme="light"] .deck-wrapper .lx-result-sum,
html[data-theme="light"] .deck-wrapper .qd-pay-value {
    color: var(--ink);
    font-weight: var(--fw-label);
    font-variant-numeric: tabular-nums;
}

/* The waiting age is a fact until it is a problem — Telefon's `.lx-when` argument, same component. */
html[data-theme="light"] .deck-wrapper .lx-when {
    color: var(--ink-muted);
    font-variant-numeric: tabular-nums;
}


/* ── P7. ZONE 3 IS STILL A STACK OF BOXES, AND THE DECISION ALREADY EXISTS ─────────────────────
   Re-derived on the render AFTER P1–P6 landed, which is the only reason it is visible: with the
   three `.qdd-zone` panels and the head/foot bands gone, what is left in TILLDELNING is three
   framed widgets in a column that also contains two unframed fields. Probed at 1900 in light:

       .diff-toggle    #ffffff              1px --keyline   r4    <- container
         .diff-seg ×3  rgba(255,255,255,.85) 1px             r4    <- controls INSIDE it
       .lift-toggle    rgba(255,255,255,.85) 1px            r12    <- a control (correct), wrong radius
       .mech-picker    rgba(255,255,255,.85) 1px            r12    <- container
         .ssel-btn     rgba(255,255,255,.85) 1px            r10    <- control INSIDE it

   A bordered box of bordered boxes, twice, in the column whose other two fields (`INTERN
   ANTECKNING`, the hint rows) are a label over a bare control. In light these read as near-white
   rather than grey, so they survived the grey census — but they are the same fault the census was
   chasing: depth spent twice for one object.

   THIS IS NOT A NEW DECISION. detail.css §"The lift toggle is a field group wearing the costume of
   a card" already yields exactly these frames — on `.modal-panel`, the booking sheet, where the same
   two components render. `.qdd-page`, `.deck-wrapper` and `.qdt-page` render them too and were never
   in that selector list. Same shape as P6b: one shared control fixed on one of its consumers.

   The CONTAINER yields and the CONTROLS keep their edges, per P1h — a control carries its own border
   by right; a container inside a card does not. `.diff-toggle` is the one that needs the check
   rather than the assumption, because `.diff-toggle.invalid` signals the required state through the
   container's `border-color`: verified in the markup that no view in this cluster binds `invalid`
   (QuoteDraftDetailDesktop.vue:204, QuoteDraftsDesktop.vue:311, QuoteDraftsTablet.vue:168 all pass
   `:tag` only), so nothing is being dropped. The required signal here is `.diff-req` and
   `.mdl-hint.warn`, both already on --st-expected from P1g/P3.

   P3's block above set `.diff-toggle`'s frame; it is EDITED there rather than superseded here, so
   there is one statement per object. */
html[data-theme="light"] .qdd-page .mech-picker {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
}

/* The two controls that legitimately keep a frame, brought to the 4px ceiling P3/P6 set for this
   cluster. `.lift-toggle` at 12 and `.ssel-btn` at 10 were the last two soft corners on the page,
   sitting beside `.diff-seg` and `.qdd-sheet` at 4. */
html[data-theme="light"] .qdd-page .lift-toggle,
html[data-theme="light"] .qdd-page .ssel-btn,
html[data-theme="light"] .qdt-record .diff-seg {
    border-radius: var(--r-control);
}

/* Two component labels join the column's ladder. With the boxes gone, "Svårighetsgrad" and "Önskad
   mekaniker" sit on the same left edge as "INTERN ANTECKNING" with nothing between them — and the
   two rungs disagreed: probed, `.mdl-label` on this page is 11px/600 uppercase at 1.1px tracking
   (the page sheet's, NOT §9's `.mdl-sheet` variant, which is sentence case and does not reach here),
   while `.diff-label` and `.mech-picker-label` are the components' own 0.82rem/600 sentence case.
   Two kinds of field label in one column. They take the page's eyebrow, measured off `.mdl-label`
   rather than copied from another file.

   The parenthetical tag stays in sentence case. `(obligatoriskt)` is amber and `(valfritt)` is
   dimmed; both are asides inside the label, and an uppercased amber "(OBLIGATORISKT)" is the loudest
   thing in the zone again — which is the fault P1g just fixed one element over. */
html[data-theme="light"] .qdd-page .diff-label,
html[data-theme="light"] .qdd-page .mech-picker-label,
html[data-theme="light"] .deck-wrapper .diff-label {
    font-size: 11px;
    font-weight: var(--fw-label);
    letter-spacing: 1.1px;
    text-transform: uppercase;
    color: var(--ink-muted);
}

/* Verified live rather than assumed: `.mech-picker`, `.lift-toggle`, `.ssel-btn` and `.mech-tag`
   match ZERO elements under `.deck-wrapper` — the console renders DifficultyToggle and nothing else
   from that family (its confirm sheet is a `.modal-panel`, already covered in §9). Those selectors
   were written and then removed rather than left speculative. */
html[data-theme="light"] .qdd-page .diff-req,
html[data-theme="light"] .qdd-page .mech-tag,
html[data-theme="light"] .deck-wrapper .diff-req {
    letter-spacing: 0;
    text-transform: none;
}

/* `.qdd-booked` — "bokad tid · 1tim" — is a 999px outlined capsule stating a fact, on the same page
   whose phone twin `.deck-booked` was rectangled by P5d for exactly this reason: a capsule is this
   theme's shorthand for a control, and this is not one. One object, two shells, one treatment.
   The `.flag` variant's status paint is already correct above and is not restated. */
html[data-theme="light"] .qdd-page .qdd-booked,
html[data-theme="light"] .deck-wrapper .qd-booked {
    border-radius: var(--r-control);
}
