/* Light-only overrides — the phone shell.
   ─────────────────────────────────────────────────────────────────────────────────────────────
   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 this 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.

   SCOPING, AND WHY IT MATTERS MORE HERE THAN ANYWHERE
   The phone sheets are the worst offenders in the app for generic class names: `.info-label`,
   `.quote-card`, `.status-badge`, `.chat-sheet`, `.detail-label`, `.glass-panel` are each declared
   by half a dozen unrelated stylesheets. Every rule below is therefore anchored to its page root
   (`.jdp-wrap`, `.fjd-phone`, `.qdp-wrapper`, `.fjp`, …) unless the class carries a page-unique
   prefix. Three phone sheets teleport a sheet to <body> (JobDetailPhone's chat, the finished-jobs
   filter/month sheets, FinishedJobDetailPhone's chat) — those elements are NOT inside the page
   root, so they are matched by their own prefixed names instead.

   ALREADY COVERED ELSEWHERE, DELIBERATELY ABSENT HERE
   `.chat-pill` / `.chat-avatar` / `.chat-send` / `.chat-takeover` / `.chat-row.customer
   .chat-bubble` / `.chat-input:focus` (registers.css, unscoped) already reach the finished-job
   detail phone chat sheet. `.mdp-*` (settings.css) covers the whole message-drafts phone view.
   `.brief-*`, `.cx-chip.on`, `.cx-key.on`, `.cx-row:hover`, `.cx-demo`, `.dp-chip.on`,
   `.dp-preset`, `.dp-more`, `.dp-quick-eyebrow`, `.detail-label`, `.lx-*`, `.lf-*`, `.ssel-*` and
   `.set*` likewise. Duplicating them here would be two owners for one decision. */


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   1. THE CLIPPED-GRADIENT PAGE TITLES, IN THEIR LOCAL DIALECTS
   ══════════════════════════════════════════════════════════════════════════════════════════════
   app.css already flattens `.purple-gradient` to ink in light — a lead-owned decision, on the
   grounds that a page heading is not the wordmark. Four phone views declined to use that class and
   wrote the same two gradient stops by hand, so the decision missed them and they are the largest
   violet object on their page. Same answer, verbatim. */
html[data-theme="light"] .vsp-hl,
html[data-theme="light"] .dp-hl,
html[data-theme="light"] .rtp-title-hl {
    background: none;
    -webkit-background-clip: initial;
    background-clip: initial;
    -webkit-text-fill-color: currentColor;
    color: var(--text);
}


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   2. /job-detail — .jdp-*
   ══════════════════════════════════════════════════════════════════════════════════════════════
   The phone twin of what detail.css did for `.detail-wrapper`, on this view's own prefixed names.
   Same roles, same tokens, so the two shells answer alike. */

/* The label column and its bullet: an eyebrow, printed beside the value it names. */
html[data-theme="light"] .jdp-info-label { color: var(--text-muted); }
html[data-theme="light"] .jdp-info-label::before {
    background: var(--border-strong);
    box-shadow: none;
}

/* The work's name is the card's own heading value, not a link. */
html[data-theme="light"] .jdp-info-value.jdp-work-name { color: var(--text); }
html[data-theme="light"] .jdp-work-added { color: var(--text-muted); }

/* These two rows ARE links (they jump to the profile), so they keep the accent. */
html[data-theme="light"] .jdp-info-value.jdp-link { color: var(--accent-solid); }
html[data-theme="light"] .jdp-info-value.jdp-link:active { color: var(--accent-solid-hover); }

/* "Bokad" is a status word, and violet is not this theme's status vocabulary. Same call jobs.css
   made for `.status-badge.booked` on the desktop board. */
html[data-theme="light"] .jdp-status-badge.booked { color: var(--text-secondary); }

/* The quote count beside the section heading. A count, not an action. */
html[data-theme="light"] .jdp-count {
    color: var(--text-muted);
    background: var(--surface-3);
    border-color: var(--border-default);
    box-shadow: none;
}

/* 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"] .jdp-routine-panel {
    background: var(--recess-a03);
    border-color: var(--border-default);
    border-left-color: var(--border-strong);
}
html[data-theme="light"] .jdp-routine-btn {
    background: var(--surface-3);
    border-color: var(--border-default);
    color: var(--text);
}

/* Recesses inside the card. `--surface-a03` is a RAISED rung — near-white in light — so a quote
   card mapped to it disappears into the panel it sits on. Recesses step down instead. */
html[data-theme="light"] .jdp-quote-card,
html[data-theme="light"] .jdp-chat-head { background: var(--recess-a03); }

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

/* The secondary pill controls, all of them a violet fill plus a violet border plus violet text. */
html[data-theme="light"] .jdp-full-quote-btn,
html[data-theme="light"] .jdp-supersede-toggle,
html[data-theme="light"] .jdp-quote-more-btn,
html[data-theme="light"] .jdp-hq-rename-btn.save,
html[data-theme="light"] .jdp-eq-chip,
html[data-theme="light"] .jdp-chat-takeover {
    background: var(--surface-3);
    border-color: var(--border-default);
    color: var(--text);
}
html[data-theme="light"] .jdp-eq-action { color: var(--accent-solid); }
html[data-theme="light"] .jdp-supersede-list { border-left-color: var(--border-default); }

/* Fields: the hairline is structure, the ring is focus. */
html[data-theme="light"] .jdp-hq-name-input { border-color: var(--border-default); }
html[data-theme="light"] .jdp-hq-name-input:focus,
html[data-theme="light"] .jdp-modal-field input:focus,
html[data-theme="light"] .jdp-modal-field textarea:focus,
html[data-theme="light"] .jdp-chat-input:focus { border-color: var(--focus-ring); }

/* The modal. `rgba(--ch-indigo-120c22, .85)` inverts to a pale-lavender wash; a dialog is
   --surface-5, and §9 requires a border alongside its elevation. The generic aliases the shared
   EquipmentPickerModal emits are matched under the page root, which is where they render. */
html[data-theme="light"] .jdp-modal-panel,
html[data-theme="light"] .jdp-wrap .modal-panel {
    background: var(--surface-5);
    border-color: var(--border-default);
}

/* The chat sheet is teleported to <body>, so it is matched by its own prefix and not by the root.
   Grade-A chrome: it covers the whole screen and everything under it is page. */
html[data-theme="light"] .jdp-chat-sheet { background: var(--glass-fill-chrome); }
/* Avatar, customer bubble and Send carried --ink on a violet gradient — near-black text on
   saturated violet. Flat accent with the ink that belongs on one, exactly as registers.css does
   for the unprefixed chat drawer. Send is the drawer's primary action and keeps the accent. */
html[data-theme="light"] .jdp-chat-avatar,
html[data-theme="light"] .jdp-chat-row.customer .jdp-chat-bubble,
html[data-theme="light"] .jdp-chat-send {
    background: var(--accent-solid);
    color: var(--text-on-accent);
}
/* The two pill rails. An active pill is a selection: quiet wash, leading bar, ordinary ink. */
html[data-theme="light"] .jdp-chat-thread-pill.active,
html[data-theme="light"] .jdp-chat-svc-item.active {
    color: var(--text);
    background: var(--surface-selected);
    border-color: var(--border-default);
    box-shadow: inset 3px 0 0 var(--accent-bar);
}
html[data-theme="light"] .jdp-chat-svc-more,
html[data-theme="light"] .jdp-chat-svc-item.active .jdp-chat-svc-more { color: var(--text-muted); }


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   3. /finished-job-detail — .fjd-phone
   ══════════════════════════════════════════════════════════════════════════════════════════════
   Every class in this sheet is generic (`.info-label`, `.quote-card`, `.hq-amount`, …), so every
   rule is anchored to the page root. The chat sheet is teleported out of it, but `.chat-sheet` is
   declared by no other stylesheet in the app and its inner parts are already handled, unscoped, by
   registers.css. */

html[data-theme="light"] .fjd-phone .info-label { color: var(--text-muted); }
html[data-theme="light"] .fjd-phone .info-label::before {
    background: var(--border-strong);
    box-shadow: none;
}
html[data-theme="light"] .fjd-phone .info-value.link-value { color: var(--accent-solid); }
html[data-theme="light"] .fjd-phone .info-row-link:focus-visible { outline-color: var(--focus-ring); }

html[data-theme="light"] .fjd-phone .quote-count {
    color: var(--text-muted);
    background: var(--surface-3);
    border-color: var(--border-default);
}
html[data-theme="light"] .fjd-phone .quote-status { color: var(--text-muted); }
html[data-theme="light"] .fjd-phone .hq-amount { color: var(--text); }

html[data-theme="light"] .fjd-phone .supersede-toggle,
html[data-theme="light"] .fjd-phone .full-quote-btn,
html[data-theme="light"] .fjd-phone .quote-more-btn {
    background: var(--surface-3);
    border-color: var(--border-default);
    color: var(--text);
}
html[data-theme="light"] .fjd-phone .supersede-toggle:hover,
html[data-theme="light"] .fjd-phone .full-quote-btn:hover,
html[data-theme="light"] .fjd-phone .quote-more-btn:hover {
    background: var(--surface-3-hover);
    border-color: var(--border-strong);
    color: var(--text);
}
html[data-theme="light"] .fjd-phone .supersede-toggle:focus-visible,
html[data-theme="light"] .fjd-phone .full-quote-btn:focus-visible,
html[data-theme="light"] .fjd-phone .quote-more-btn:focus-visible { outline-color: var(--focus-ring); }

/* Recesses inside the card, off the raised rung they were mapped to. */
html[data-theme="light"] .fjd-phone .quote-card,
html[data-theme="light"] .fjd-phone .notes-block,
html[data-theme="light"] .fjd-phone .full-quote-inline { background: var(--recess-a03); }
html[data-theme="light"] .fjd-phone .info-row-link:hover { background: var(--surface-hover); }

/* The full-screen chat sheet, teleported to <body>. Same grade-A chrome as the job-detail one. */
html[data-theme="light"] .chat-sheet { background: var(--glass-fill-chrome); }
html[data-theme="light"] .chat-sheet .chat-head { background: var(--recess-a03); }


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   4. /quote-drafts — .qdp-wrapper (the incoming console + the history tabs)
   ══════════════════════════════════════════════════════════════════════════════════════════════
   Note that /quote-draft-detail's phone view declares the SAME root class. Their class vocabularies
   do not overlap and only one is ever mounted, so the two blocks below are independent. */

/* The origin chip and the age pill: violet as an identity hue, restating a word already printed. */
html[data-theme="light"] .qdp-wrapper .qh-source.src-agent,
html[data-theme="light"] .qdp-wrapper .time-pill {
    color: var(--text-muted);
    background: var(--surface-3);
    border-color: var(--border-default);
    box-shadow: none;
}
html[data-theme="light"] .qdp-card { border-color: var(--border-subtle); }

/* Eyebrows, chevrons and counts down the card. */
html[data-theme="light"] .qdp-wrapper .card-more,
html[data-theme="light"] .qdp-wrapper .catchup-label,
html[data-theme="light"] .qdp-wrapper .catchup-chev { color: var(--text-muted); }
html[data-theme="light"] .qdp-wrapper .card-catchup { border-left-color: var(--border-strong); }

/* Money is money, not a link. */
html[data-theme="light"] .qdp-wrapper .detail-value.cost,
html[data-theme="light"] .qdp-wrapper .dlr-price { color: var(--text); }

/* The total block was a violet-to-violet gradient — the loudest object on a card whose actual
   primary is "Bekräfta & Skicka" underneath it. A recess with a hairline says "summed" without
   competing with the button. */
html[data-theme="light"] .qdp-wrapper .deck-total {
    background: var(--recess-a03);
    border-color: var(--border-default);
}
html[data-theme="light"] .qdp-wrapper .dt-rows,
html[data-theme="light"] .qdp-wrapper .dt-row.grand { border-top-color: var(--border-default); }

/* The hours field's focus, and the filter list's selection. */
html[data-theme="light"] .qdp-wrapper .dlr-hours:focus-within {
    border-color: var(--focus-ring);
    box-shadow: 0 0 0 3px var(--accent-tint-strong);
}
html[data-theme="light"] .qdp-wrapper .qdp-filter-item.active {
    color: var(--text);
    background: var(--surface-selected);
    box-shadow: inset 3px 0 0 var(--accent-bar);
}


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   5. /quote-draft-detail — .qdp-* (the editor)
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* Text buttons that really are actions keep the accent; the fold chevron beside a heading is
   decoration and takes ink. */
html[data-theme="light"] .qdp-regen-btn,
html[data-theme="light"] .qdp-msg-rendering,
html[data-theme="light"] .qdp-eq-field-edit { color: var(--accent-solid); }
html[data-theme="light"] .qdp-chev { color: var(--text-muted); }

/* Totals are money. */
html[data-theme="light"] .qdp-sec-peek,
html[data-theme="light"] .qdp-line-total,
html[data-theme="light"] .qdp-b-row.grand span:last-child { color: var(--text); }

/* The section folds are recesses in the panel, not raised chips. */
html[data-theme="light"] .qdp-sec-toggle { background: var(--recess-a03); }

/* Fields. Every ring on this page was a 15%-violet halo plus a violet border; the border is the
   ring in light, and the halo goes to the system's own tint. */
html[data-theme="light"] .qdp-field textarea:focus,
html[data-theme="light"] .qdp-lf-box:focus-within {
    border-color: var(--focus-ring);
    box-shadow: 0 0 0 3px var(--accent-tint-strong);
}
html[data-theme="light"] .qdp-line-name:focus {
    background: var(--field-bg-hover);
    border-color: var(--focus-ring);
}
html[data-theme="light"] .qdp-eq-field-btn:active { border-color: var(--border-strong); }

/* "Lägg till rad" is a secondary, dashed, add-a-thing control — not the page's decision. */
html[data-theme="light"] .qdp-add-line {
    background: var(--recess-a03);
    border-color: var(--border-default);
    color: var(--text-muted);
}
html[data-theme="light"] .qdp-add-line:active {
    background: var(--surface-hover);
    border-color: var(--border-strong);
}
html[data-theme="light"] .qdp-more { border-left-color: var(--border-default); }

/* The confirm sheet: a dialog, on the dialog rung. */
html[data-theme="light"] .qdp-sheet {
    background: var(--surface-5);
    border-color: var(--border-default);
}


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   6. /finished-jobs — .fjp-*
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* The filter toggle is a selection, and its count is a count. The badge was `--astra-accent` under
   `--ink`, which in light is near-black type on saturated violet. */
html[data-theme="light"] .fjp-filters-btn.active {
    color: var(--text);
    background: var(--surface-selected);
    border-color: var(--border-default);
    box-shadow: inset 3px 0 0 var(--accent-bar);
}
html[data-theme="light"] .fjp-filters-badge {
    background: var(--surface-3);
    border: 1px solid var(--border-default);
    color: var(--text-muted);
}
html[data-theme="light"] .fjp-opt.active {
    color: var(--text);
    background: var(--surface-selected);
    box-shadow: inset 3px 0 0 var(--accent-bar);
}

/* The two teleported sheets. `rgba(--ch-indigo-0f0a1a, .97)` inverts to a pale lavender slab. */
html[data-theme="light"] .fjp-sheet {
    background: var(--surface-5);
    border-color: var(--border-default);
}

/* Slabs and recesses. `0 4px 24px rgba(--ref-shadow-base, .3)` is a 30% near-black drop under a
   card on a white page; the elevation scale is what light has for this. */
html[data-theme="light"] .fjp-lot { box-shadow: var(--elevation-1); }
html[data-theme="light"] .fjp-group,
html[data-theme="light"] .fjp-agenda-item,
html[data-theme="light"] .fjp-day-label,
html[data-theme="light"] .fjp-call.none { background: var(--recess-a03); }
html[data-theme="light"] .fjp-row:active { background: var(--surface-hover); }

/* Focus was drawn in the emerald this page uses for "collected" — a status colour standing in for
   a focus ring, which §6 gives one token. */
html[data-theme="light"] .fjp-lot:focus-visible { outline-color: var(--focus-ring); }
html[data-theme="light"] .fjp-row:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: -2px;
    background: var(--surface-hover);
    box-shadow: none;
}


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   7. /jobs — .jp-* (chrome), .jpa-* (the day as a list), .jpb-* (the day as bars)
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* The selected weekday and the layout switch: two selections, both painted as a violet field. */
html[data-theme="light"] .jp-tab.is-on,
html[data-theme="light"] .jp-viewswitch button.on {
    color: var(--text);
    background: var(--surface-selected);
    border-color: var(--border-default);
    box-shadow: inset 3px 0 0 var(--accent-bar);
}
/* Today's number keeps the accent — same call jobs.css made for the desktop calendar's today mark.
   The load dot does not: it is one rung of a green/violet/amber/red ramp, and violet is the only
   member of it that is not a status. */
html[data-theme="light"] .jp-tab.is-today .jp-tab-num { color: var(--accent-solid); }
html[data-theme="light"] .jp-tab-dot.busy { background: var(--st-info); }

/* A booked card, on the same two channels the desktop board's booked events use. */
html[data-theme="light"] .jpa-card.status-booked {
    background: rgba(var(--wev-booked-wash), 0.5);
    border-color: rgba(var(--wev-booked-edge), 0.28);
    border-left-color: rgb(var(--wev-booked-edge));
}
html[data-theme="light"] .jpa-card .status-badge.booked {
    background: var(--recess-a05);
    border-color: var(--border-default);
    color: var(--text-secondary);
    box-shadow: none;
}
/* The work-queue mark, the "om 40 min" relative time and the two provenance tags: three decorative
   violets on a card whose own status already rides its left edge. */
html[data-theme="light"] .jpa-card.is-wq { border-left-color: var(--border-strong); }
html[data-theme="light"] .jpa-time-rel { color: var(--text-muted); }
html[data-theme="light"] .jpa-wq,
html[data-theme="light"] .jpa-dropoff {
    background: var(--recess-a05);
    color: var(--text-muted);
}


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   8. /work-queue, /vehicle-storage, /routines, /quote-history-detail — the read-only summaries
   ══════════════════════════════════════════════════════════════════════════════════════════════
   Four phone views that exist to state a number and name the surface where the work happens. None
   of them has a primary action, so none of them has anything violet to earn. */

html[data-theme="light"] .wqp-backlog,
html[data-theme="light"] .wqp-lane {
    background: var(--surface-1);
    border-color: var(--border-default);
}

html[data-theme="light"] .vsp-badge,
html[data-theme="light"] .vsp-stat-value {
    color: var(--text-muted);
    background: var(--surface-3);
    border-color: var(--border-default);
    box-shadow: none;
}
html[data-theme="light"] .vsp-retry {
    color: var(--text);
    background: var(--surface-3);
    border-color: var(--border-default);
}
html[data-theme="light"] .vsp-retry:hover:not(:disabled) {
    background: var(--surface-3-hover);
    border-color: var(--border-strong);
}

html[data-theme="light"] .rtp-wrap :focus-visible { outline-color: var(--focus-ring); }

html[data-theme="light"] .qhp-source.src-agent {
    color: var(--text-muted);
    background: var(--surface-3);
    border-color: var(--border-default);
}
html[data-theme="light"] .qhp-time-label,
html[data-theme="light"] .qhp-wait-label { color: var(--text-muted); }
/* The grand total: the page's headline figure, tinted violet for emphasis. Emphasis in light comes
   from the surface step and the hairline, not from the accent. */
html[data-theme="light"] .qhp-total {
    background: var(--recess-a03);
    border-color: var(--border-default);
}
html[data-theme="light"] .qhp-wait { background: var(--recess-a03); }


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   9. /login and /set-password — the two pages outside the shell
   ══════════════════════════════════════════════════════════════════════════════════════════════ */

/* The sign-in card painted `rgba(--sf-rgb, .05)`; that channel inverts to the ink colour, so the
   panel rendered as a 5% grey wash over the violet ground instead of as a card. Grade-A chrome. */
html[data-theme="light"] .login-wrapper .glass-panel {
    background: var(--glass-fill-chrome);
    border-color: var(--glass-edge-chrome);
    box-shadow: var(--elevation-2);
}

/* ── THE SIGN-IN CARD IS NOT A DATA SURFACE ───────────────────────────────────────────────────
   Measured against dark at 1900, and the gap was not a matter of taste:

                        dark                 light (before)
     .glass-panel       400x477 r24 pad 40px 32px    400x431 r0  pad 40px **0px**
     .field input       334x44  r12                  400x28  r4
     .btn-astra         334x45  r12                  400x36  r4
     .badge             r50 (pill) pad 4px 12px      r4  pad 2px 6px

   TWO SEPARATE FAULTS, and the first is a plain bug. The card's HORIZONTAL PADDING WAS ZERO, so
   every field ran the full 400px edge to edge instead of dark's 334px — the fields were literally
   touching the card's border. That is the whole "not enough spacing": the app-wide un-carding rule
   ("a nested block inside a card gets no frame at all") reached a card that is not nested in
   anything. A centred sign-in panel on an empty page IS the outermost surface.

   The second is the 4px radius ceiling. That ceiling is argued from Stripe's DASHBOARD — a dense
   operations surface where softness comes from the shadow because there are hundreds of corners on
   screen. This page has four. One card, centred, on an empty ground, and nothing to be dense
   against. Applying a density rule to it produced a sharp-cornered white rectangle, which is the
   one shape a sign-in card should never be.

   So the geometry comes back to dark's, and only the SURFACE is light: white card, keyline,
   elevation, ink on white. Same shapes, same rhythm, light paint. Radii are stated as literals
   rather than tokens on purpose — `--r-card`/`--r-control` are the 4px density ceiling and this
   page is deliberately not on it. */
html[data-theme="light"] .login-wrapper .glass-panel {
    padding: 40px 32px;
    border-radius: 24px;
}

/* The wordmark chip is a pill in dark and reads as one object; at 4px with 2px of padding it had
   become a grey label stuck to the corner. */
html[data-theme="light"] .login-wrapper .badge {
    border-radius: 999px;
    padding: 4px 12px;
    font-size: 0.6875rem;
}

/* 28px is a dense-table row height, not a field somebody types an email into. Dark is 44, which is
   also the app's touch floor — this page is the one surface a mechanic uses on a phone before the
   shell exists, so the floor genuinely applies. */
html[data-theme="light"] .login-wrapper .field input {
    min-height: 44px;
    border-radius: 12px;
    padding: 10px 14px;
}

/* The `:not()` chain is copied EXACTLY from `_base.css`'s primary-button recipe, which scores
   (0,5,1). A comfortable `.login-wrapper .btn-astra` scores (0,3,1) and silently loses — written
   that way first, and the button rendered at 36px/4px while every other rule in this block landed.
   `:not()` contributes its argument's specificity; that is trap #16 and it has now cost this
   programme three separate rules. */
html[data-theme="light"] .login-wrapper .btn-astra:not(.btn-astra-neutral):not(.btn-astra-glass):not(.btn-astra-danger) {
    min-height: 45px;
    border-radius: 12px;
    font-size: 0.9375rem;
}

html[data-theme="light"] .login-wrapper .link-btn {
    border-radius: 8px;
}
html[data-theme="light"] .login-wrapper .link-btn {
    color: var(--accent-solid);
    text-decoration-color: var(--accent-hairline);
}
html[data-theme="light"] .login-wrapper .field input:focus {
    border-color: var(--focus-ring);
    box-shadow: 0 0 0 3px var(--accent-tint-strong);
}

/* `--c-indigo-f4f4f8` is one of the channels that inverts to #5400b6 — a saturated violet well
   outside this theme's accent. It was painting BOTH the page's heading and the text a person types
   into the password fields. The field itself was `rgba(--ref-shadow-base, .28)`, i.e. a 28%
   near-black slab standing in for an input. */
html[data-theme="light"] .setpw-phone h1,
html[data-theme="light"] .setpw-phone-field input { color: var(--text); }
html[data-theme="light"] .setpw-phone-field input {
    background: var(--field-bg);
    border-color: var(--field-border);
}
html[data-theme="light"] .setpw-phone-field input:focus-visible { outline-color: var(--focus-ring); }


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   10. THE GAPS THE BOARD CLUSTERS' FILES DO NOT REACH
   ══════════════════════════════════════════════════════════════════════════════════════════════
   boards.css and settings.css were written against the desktop views of /complaints, /drop-offs and
   /morning-brief. Their class vocabularies overlap the phone views' almost entirely — which is why
   those two files already do most of the work here — but each phone view carries a few controls the
   desktop does not, and those are what is left. */

/* ComplaintsPhone's lane strip is `.cx-tab`; the desktop's is `.cx-chip`, which boards.css covers.
   Same control, same answer. */
html[data-theme="light"] .cx-wrap .cx-tab.on {
    color: var(--text);
    background: var(--surface-selected);
    border-color: var(--accent-hairline);
    box-shadow: inset 3px 0 0 var(--accent-bar);
}
html[data-theme="light"] .cx-wrap .cx-tab.on > b { color: var(--text-muted); }
/* `.cx-title > span` DELETED. It painted the count inside the /complaints h1 in --accent-solid, and
   because this file loads last and the rule carried no shell scope and no media query, it beat
   boards.css's `.cx-title > span { color: var(--ink) }` on DESKTOP as well as on the phone — a
   phone-file rule silently overturning a desktop decision. The count is a number in a heading, not
   an action, so it does not get the accent on any shell. boards.css is unscoped and already covers
   all three views; there is nothing phone-specific left to say. */

/* The demo switches on /complaints and /morning-brief. A switch's lit track IS an accent fill —
   that is one of the four things violet is for — but at 55% of a channel that inverts to #6d28d9 it
   was neither the accent nor a flat colour. */
html[data-theme="light"] .cx-demo-toggle.on .cx-demo-track,
html[data-theme="light"] .brief-demo-toggle.on .brief-demo-track { background: var(--accent-solid); }
html[data-theme="light"] .cx-demo-toggle.on,
html[data-theme="light"] .brief-demo-toggle.on { border-color: var(--accent-hairline); }

/* The bars view's default spine is `--ink-faint` — a TEXT token in a border role, which is the
   ink-as-fill defect the gate names: it inverts with the ink ramp, so the one edge that means
   "no status yet" tracks the type colour instead of the border scale. Repointing the sheet would
   move dark (the two tokens are .38 and .18 there), so light takes the border token here. */
html[data-theme="light"] .jpb-bar { border-left-color: var(--border-strong); }

/* Two more raised rungs standing in for recesses. */
html[data-theme="light"] .cx-wrap .cx-list,
html[data-theme="light"] .brief-fold-head { background: var(--recess-a03); }


/* ══════════════════════════════════════════════════════════════════════════════════════════════
   11. THE STRUCTURAL PASS — the Work Queue prototype, mapped onto the phone shell
   ══════════════════════════════════════════════════════════════════════════════════════════════
   Sections 1-10 above are the COLOUR pass: they stopped the phone views reading violet. This one is
   the STRUCTURAL pass, and it is what boards.css's "WORK QUEUE — the light structural prototype"
   did to that page, applied here to the phone views' own names.

   It is scoped to the four views of the jobs cluster — /jobs (`.jp-*`, `.jpa-*`, `.jpb-*`),
   /finished-jobs (`.fjp-*`), /job-detail (`.jdp-*`) and /finished-job-detail (`.fjd-phone`) — plus
   the generic classes those views declare, which are always anchored to a page root.

   Every selector was read out of the phone sheet or its template. Nothing is guessed: on a phone
   view a wrong selector is doubly invisible, because there is no desktop screenshot that would show
   it failing.

   ONE PHONE-SPECIFIC RULE OVERRIDES THE DENSITY LADDER: nothing here goes below a 44px hit target.
   `--row-h` is a painted height, not a touch target, so where the two disagree the target wins.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */


/* ── P1. THE STICKY WEEK BAR IS CHROME ─────────────────────────────────────────────────────────
   `.jp-weekbar` is glass over the scrolling day: `--glass-bg` plus `--glass-blur` plus a
   `--glass-border`. In light the blur is `none` by token and the fill is white, so what is left is
   an opaque bar whose only separation from the content sliding under it is that hairline — which is
   exactly the Stripe answer, and it just needs the tokens saying so rather than the glass ones. */
html[data-theme="light"] .jp-weekbar {
    background: var(--surface);
    border-bottom: 1px solid var(--keyline);
}

/* ── P2. THE WEEKDAY STRIP ─────────────────────────────────────────────────────────────────────
   Seven tabs across 412px. Rectangles at the control rung; the 48px floor stays, because it is a hit
   target and not a density decision. Section 7 above already gives `.jp-tab.is-on` its selection
   wash and leading bar — this is the shape and the figures. */
html[data-theme="light"] .jp-tab {
    border-radius: var(--r-control);
}
html[data-theme="light"] .jp-tab-num {
    font-variant-numeric: tabular-nums;
    font-weight: var(--fw-label);
}
html[data-theme="light"] .jp-tab-name {
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
    color: var(--ink-muted);
}

/* The load dot is a 5px graphical object with no word beside it, so it owes 3:1 and NOT 4.5:1 —
   which is the whole reason the --meter-* family exists. The status TEXT tokens render a 5px dot as
   olive-brown and washed pink. `.busy` is deliberately absent: section 7 above already re-pointed it
   at --st-info on the grounds that violet is not a status, and that decision stands. */
html[data-theme="light"] .jp-tab-dot.quiet { background: var(--meter-ok); }
html[data-theme="light"] .jp-tab-dot.full { background: var(--meter-warn); }
html[data-theme="light"] .jp-tab-dot.over { background: var(--meter-bad); }

/* The day-layout switch: a track with two buttons. Flat track, rectangles inside it. */
html[data-theme="light"] .jp-viewswitch {
    background: none;
    border-color: var(--keyline);
    border-radius: var(--r-control);
}
html[data-theme="light"] .jp-viewswitch button {
    border-radius: var(--r-control);
    font-weight: var(--fw-label);
}
html[data-theme="light"] .jp-nav-btn {
    border-color: var(--keyline-strong);
    border-radius: var(--r-control);
    color: var(--ink-dim);
}
html[data-theme="light"] .jp-nav-btn:hover {
    background: var(--surface-inset);
    color: var(--ink);
}
html[data-theme="light"] .jp-daycount {
    color: var(--ink-muted);
    font-weight: var(--fw-label);
    font-variant-numeric: tabular-nums;
}


/* ── P3. THE DAY AS A LIST — `.jpa-*` ──────────────────────────────────────────────────────────
   The card keeps its 3px status edge; that is good structure and it survives the theme. What it
   loses is the consumer radius. The status wash stays: on this view the tint IS the status, the
   card has no other room for one, and the `--ch-*` channels behind it are theme-aware. */
html[data-theme="light"] .jpa-card {
    border-radius: var(--r-card);
}
html[data-theme="light"] .jpa-time-range,
html[data-theme="light"] .jpa-lunch-time {
    font-variant-numeric: tabular-nums;
}

/* `JobsPhoneAgenda.css` declares `.status-badge` UNSCOPED — a global name in a phone sheet — so
   every rule below is anchored to `.jpa-card`, which is where it renders. The classes come from
   `jobStatusBadgeClass()` (src/models/jobStatus.ts): in-progress · awaiting-pickup · booked ·
   canceled · finished · awaiting-checkin. `.pending` is declared by the sheet and never emitted; it
   is mapped anyway so a stale value cannot fall through to a dark tint.

   Badge grammar with a SHAPE-distinct glyph, same assignment as the desktop board in jobs.css, so a
   mechanic who learns the phone learns the desktop. */
html[data-theme="light"] .jpa-card .status-badge {
    border-radius: 4px;
    padding: 2px 6px;
    font-size: 0.6875rem;
    font-weight: var(--fw-label);
    box-shadow: none;
}
html[data-theme="light"] .jpa-card .status-badge.in-progress {
    background: var(--st-logged-soft);
    border: 1px solid var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .jpa-card .status-badge.in-progress::before { content: "\25B8"; }
html[data-theme="light"] .jpa-card .status-badge.finished {
    background: var(--st-logged-soft);
    border: 1px solid var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .jpa-card .status-badge.finished::before { content: "\2713"; }
html[data-theme="light"] .jpa-card .status-badge.awaiting-pickup,
html[data-theme="light"] .jpa-card .status-badge.awaiting-checkin {
    background: var(--surface-inset);
    border: 1px solid var(--keyline-strong);
    color: var(--ink-dim);
}
html[data-theme="light"] .jpa-card .status-badge.awaiting-pickup::before,
html[data-theme="light"] .jpa-card .status-badge.awaiting-checkin::before { content: "!"; }
html[data-theme="light"] .jpa-card .status-badge.booked,
html[data-theme="light"] .jpa-card .status-badge.pending {
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    color: var(--ink-dim);
}
html[data-theme="light"] .jpa-card .status-badge.booked::before,
html[data-theme="light"] .jpa-card .status-badge.pending::before { content: "\2022"; }
html[data-theme="light"] .jpa-card .status-badge.canceled {
    background: var(--surface-inset);
    border: 1px solid var(--keyline-strong);
    color: var(--ink-dim);
}
html[data-theme="light"] .jpa-card .status-badge.canceled::before { content: "\2715"; }


/* ── P4. THE DAY AS BARS — `.jpb-*` ────────────────────────────────────────────────────────────
   `.jpb-lunch` is `rgba(var(--sf-rgb), 0.028)`: the SURFACE channel, which inverts to the ink
   channel in light. The lunch window — the thing the bars are drawn around — was therefore a grey
   smear in light where it is a faint lift in dark. It is a recess either way, and light has a token
   that says so. */
html[data-theme="light"] .jpb-lunch {
    background: var(--surface-inset);
}
html[data-theme="light"] .jpb-bar {
    border-radius: var(--r-control);
}
html[data-theme="light"] .jpb-hour-lbl,
html[data-theme="light"] .jpb-time {
    font-variant-numeric: tabular-nums;
}
/* The "now" mark. Its dot sits directly on the rule; a 2px ring in the surface colour punches it
   out instead of letting the two shapes merge at the overlap. */
html[data-theme="light"] .jpb-now-dot {
    box-shadow: 0 0 0 2px var(--surface);
}


/* ── P5. /finished-jobs — the lanes, the lot and the ledger (`.fjp-*`) ─────────────────────────
   This view mirrors the desktop board class for class, so it takes the same decisions. Any change
   here that is not also in jobs.css is a bug in one of the two.

   The three lane buttons are a selector, not tabs — they are 52px targets carrying a count, and on
   a phone that box is the affordance. So the box stays and only the paint changes: the active lane
   is an inset fill with an accent border, and the emerald/blue tints go. */
html[data-theme="light"] .fjp-lane {
    border-radius: var(--r-card);
    border-color: var(--keyline);
    background: var(--surface);
}
html[data-theme="light"] .fjp-lane.active.awaiting,
html[data-theme="light"] .fjp-lane.active.collected,
html[data-theme="light"] .fjp-lane.active.canceled {
    background: var(--surface-inset);
    border-color: var(--accent);
    color: var(--ink);
}
html[data-theme="light"] .fjp-lane.active.awaiting .fjp-lane-count,
html[data-theme="light"] .fjp-lane.active.collected .fjp-lane-count,
html[data-theme="light"] .fjp-lane.active.canceled .fjp-lane-count {
    color: var(--ink);
}
html[data-theme="light"] .fjp-lane-name {
    font-weight: var(--fw-label);
}

html[data-theme="light"] .fjp-segment {
    background: none;
    border-color: var(--keyline);
    border-radius: var(--r-control);
}
html[data-theme="light"] .fjp-seg-btn {
    border-radius: var(--r-control);
    font-weight: var(--fw-label);
}
html[data-theme="light"] .fjp-seg-btn.active {
    background: var(--surface-inset);
    border-color: var(--accent);
    color: var(--ink);
}

/* The lot card: glass and a 24px drop shadow off, keyline on, radius to the card rung. */
html[data-theme="light"] .fjp-lot {
    background: var(--surface);
    border: 1px solid var(--keyline);
    border-radius: var(--r-card);
    box-shadow: none;
}
html[data-theme="light"] .fjp-lot:focus-visible {
    outline: 2px solid var(--accent);
}

/* THE VERDICT RULE. Every card in the lane prints a wait, so amber on `wait-due` is amber on nearly
   every card, and a colour that is always on screen has stopped carrying information. Ordinary goes
   to ink; only the seven-day card keeps a hue. The figure is printed in words beside the bar. */
html[data-theme="light"] .fjp-lot-wait-text {
    font-weight: var(--fw-label);
    color: var(--ink-muted);
    font-variant-numeric: tabular-nums;
}
html[data-theme="light"] .fjp-lot.wait-due .fjp-lot-wait-text {
    color: var(--ink-dim);
}
html[data-theme="light"] .fjp-lot.wait-late .fjp-lot-wait-text {
    color: var(--st-breach);
    font-weight: var(--fw-strong);
}

/* The meter: an inset track with a keyline, and fills off --meter-*. Never the --st-* text family —
   a bar owes 3:1 under SC 1.4.11 and the text tokens overshoot it into olive-brown and washed pink.
   The track keeps its full round: on a 4px-tall bar that IS the correct terminal shape. */
html[data-theme="light"] .fjp-meter {
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    box-sizing: border-box;
}
html[data-theme="light"] .fjp-lot.wait-fresh .fjp-meter-fill { background: var(--meter-ok); }
html[data-theme="light"] .fjp-lot.wait-due .fjp-meter-fill { background: var(--meter-warn); }
html[data-theme="light"] .fjp-lot.wait-late .fjp-meter-fill { background: var(--meter-bad); }

html[data-theme="light"] .fjp-call {
    color: var(--ink-dim);
    font-variant-numeric: tabular-nums;
}
html[data-theme="light"] .fjp-invoice-btn {
    border-radius: var(--r-control);
    font-weight: var(--fw-label);
}
html[data-theme="light"] .fjp-invoice-pill {
    border-radius: 4px;
    font-weight: var(--fw-label);
    background: var(--st-logged-soft);
    border: 1px solid var(--st-logged-line);
    color: var(--st-logged);
}

/* The ledger. Same `--hl-rgb` divider defect as the desktop's `.fj-row`: the separator is white at
   4.5% on white, so in light the list has no dividers at all and nothing errors. A real keyline. */
html[data-theme="light"] .fjp-group {
    background: var(--surface);
    border: 1px solid var(--keyline);
    border-radius: var(--r-card);
}
html[data-theme="light"] .fjp-row + .fjp-row {
    box-shadow: none;
    border-top: 1px solid var(--keyline);
}
html[data-theme="light"] .fjp-row {
    grid-template-columns: 16px 1fr auto;
    padding: 12px var(--row-pad-x);
}
html[data-theme="light"] .fjp-row:active {
    background: var(--surface-inset);
}
html[data-theme="light"] .fjp-row:focus-visible {
    background: var(--surface-inset);
    box-shadow: inset 2px 0 0 var(--accent);
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* The flag becomes a badge with a glyph, and the row's first column widens to match. `.due` is the
   ordinary outstanding case (every unbilled row, once a shop connects Fortnox) so it goes neutral;
   `.late` — nobody turned up — is the one that is actually wrong and keeps the hue. A clean row
   carries no modifier and paints nothing, which is the sheet's "nothing to do here" signal. */
html[data-theme="light"] .fjp-row-flag {
    width: 16px;
    height: 16px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    box-shadow: none;
    font-size: 10px;
    font-weight: var(--fw-label);
    line-height: 1;
}
html[data-theme="light"] .fjp-row-flag.due {
    background: var(--surface-inset);
    border-color: var(--keyline-strong);
    color: var(--ink-dim);
}
html[data-theme="light"] .fjp-row-flag.due::before { content: "!"; }
html[data-theme="light"] .fjp-row-flag.late {
    background: var(--st-breach-soft);
    border-color: var(--st-breach-line);
    color: var(--st-breach);
}
html[data-theme="light"] .fjp-row-flag.late::before { content: "\2715"; }

html[data-theme="light"] .fjp-bucket-name {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
    color: var(--ink-muted);
}
html[data-theme="light"] .fjp-bucket-n {
    font-weight: var(--fw-label);
    color: var(--ink-muted);
    font-variant-numeric: tabular-nums;
}
html[data-theme="light"] .fjp-row-mid .cost {
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}
html[data-theme="light"] .fjp-chip {
    border-radius: 4px;
    font-weight: var(--fw-label);
}
html[data-theme="light"] .fjp-chip.warn {
    background: var(--st-breach-soft);
    border: 1px solid var(--st-breach-line);
    color: var(--st-breach);
}

/* "Fakturerad" is a state and takes the tick. "Ofakturerad" fires on every unbilled row, so it is
   the ordinary case and goes neutral rather than amber. */
html[data-theme="light"] .fjp-inv {
    border-radius: 4px;
    font-weight: var(--fw-label);
}
html[data-theme="light"] .fjp-inv.yes {
    background: var(--st-logged-soft);
    border: 1px solid var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .fjp-inv.yes::before {
    content: "\2713";
    margin-right: 4px;
}
html[data-theme="light"] .fjp-inv.due {
    background: var(--surface-inset);
    border: 1px solid var(--keyline-strong);
    color: var(--ink-dim);
}
html[data-theme="light"] .fjp-inv.due::before {
    content: "!";
    margin-right: 4px;
}

/* The calendar agenda's status pill. The sheet declares `.ongoing`, `.pending`, `.canceled`,
   `.finished`, `.awaiting-pickup` and `.no-show`, but the template feeds it `statusBadgeOf()`, which
   ALSO emits `in-progress`, `booked` and `awaiting-checkin` — three values with no declaration in
   either theme (reported). Light covers all of them so none falls through unpainted. */
html[data-theme="light"] .fjp-agenda-status {
    border-radius: 4px;
    padding: 2px 6px;
    font-weight: var(--fw-label);
    box-shadow: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
html[data-theme="light"] .fjp-agenda-status.ongoing,
html[data-theme="light"] .fjp-agenda-status.in-progress {
    background: var(--st-logged-soft);
    border: 1px solid var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .fjp-agenda-status.ongoing::before,
html[data-theme="light"] .fjp-agenda-status.in-progress::before { content: "\25B8"; }
html[data-theme="light"] .fjp-agenda-status.finished {
    background: var(--st-logged-soft);
    border: 1px solid var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .fjp-agenda-status.finished::before { content: "\2713"; }
html[data-theme="light"] .fjp-agenda-status.awaiting-pickup,
html[data-theme="light"] .fjp-agenda-status.awaiting-checkin {
    background: var(--surface-inset);
    border: 1px solid var(--keyline-strong);
    color: var(--ink-dim);
}
html[data-theme="light"] .fjp-agenda-status.awaiting-pickup::before,
html[data-theme="light"] .fjp-agenda-status.awaiting-checkin::before { content: "!"; }
html[data-theme="light"] .fjp-agenda-status.pending,
html[data-theme="light"] .fjp-agenda-status.booked {
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    color: var(--ink-dim);
}
html[data-theme="light"] .fjp-agenda-status.pending::before,
html[data-theme="light"] .fjp-agenda-status.booked::before { content: "\2022"; }
html[data-theme="light"] .fjp-agenda-status.canceled {
    background: var(--surface-inset);
    border: 1px solid var(--keyline-strong);
    color: var(--ink-dim);
}
html[data-theme="light"] .fjp-agenda-status.canceled::before { content: "\2715"; }
html[data-theme="light"] .fjp-agenda-status.no-show {
    background: var(--st-breach-soft);
    border: 1px solid var(--st-breach-line);
    color: var(--st-breach);
}
html[data-theme="light"] .fjp-agenda-status.no-show::before { content: "\2715"; }

/* The agenda list is card-per-row: eight bordered boxes with an 8px gap where there is one list.
   It becomes a bordered scroll surface with rows divided by a single keyline. The gap has to go in
   the same breath as the divider — a 1px rule with 8px of air under it is a floating line, not a
   separator. The 60px row height is a touch target and stays as authored. */
html[data-theme="light"] .fjp-agenda-list {
    gap: 0;
    background: var(--surface);
    border: 1px solid var(--keyline);
    border-radius: var(--r-card);
    overflow: hidden;
}
html[data-theme="light"] .fjp-agenda-item {
    background: none;
    border: 0;
    border-bottom: 1px solid var(--keyline);
    border-radius: 0;
    padding: 10px var(--row-pad-x);
}
html[data-theme="light"] .fjp-agenda-item:last-child {
    border-bottom: 0;
}
html[data-theme="light"] .fjp-agenda-time {
    font-variant-numeric: tabular-nums;
    font-weight: var(--fw-label);
}
/* NOT an eyebrow: `.fjp-day-label` is the 44px BUTTON that opens the month picker, not a section
   heading. It only loses the capsule. (Named here because it reads like a label and the eyebrow
   recipe would have shrunk the date to 11px of grey uppercase.) */
html[data-theme="light"] .fjp-day-label {
    border-radius: var(--r-control);
    border-color: var(--keyline-strong);
    background: var(--surface);
}
html[data-theme="light"] .fjp-month-cell {
    font-variant-numeric: tabular-nums;
}


/* ── P6. /job-detail — `.jdp-*` ────────────────────────────────────────────────────────────────
   The page card is glass with a 40px drop shadow and a `--hl-rgb` lit top edge. On an off-white
   canvas a bordered card does not also need to float; the keyline is the whole separation. */
html[data-theme="light"] .jdp-card {
    background: var(--surface);
    border: 1px solid var(--keyline);
    border-radius: var(--r-card);
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* The record's status is a 6px coloured dot plus coloured text — colour working alone, twice. Under
   deuteranopia "Pågår" and "Avbokad" are one olive. It takes the same badge grammar as every other
   status in the app: rectangle, soft fill, tint border, dark ink, and a shape-distinct glyph.

   The sheet's own `::before` is that dot (`content: ""` with a width, a height, a radius and
   `background: currentColor`), so all four have to be undone before the glyph can be drawn — a
   `content` override alone would render the character inside a 6px circle of its own colour. */
html[data-theme="light"] .jdp-status-badge {
    border-radius: 4px;
    padding: 2px 6px;
    gap: 4px;
    font-size: 0.6875rem;
    font-weight: var(--fw-label);
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    color: var(--ink-dim);
}
html[data-theme="light"] .jdp-status-badge::before {
    width: auto;
    height: auto;
    border-radius: 0;
    background: none;
    font-size: 1em;
    line-height: 1;
}
html[data-theme="light"] .jdp-status-badge.ongoing,
html[data-theme="light"] .jdp-status-badge.in-progress {
    background: var(--st-logged-soft);
    border-color: var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .jdp-status-badge.ongoing::before,
html[data-theme="light"] .jdp-status-badge.in-progress::before { content: "\25B8"; }
html[data-theme="light"] .jdp-status-badge.finished {
    background: var(--st-logged-soft);
    border-color: var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .jdp-status-badge.finished::before { content: "\2713"; }
html[data-theme="light"] .jdp-status-badge.awaiting-pickup,
html[data-theme="light"] .jdp-status-badge.awaiting-checkin {
    background: var(--surface-inset);
    border-color: var(--keyline-strong);
    color: var(--ink-dim);
}
html[data-theme="light"] .jdp-status-badge.awaiting-pickup::before,
html[data-theme="light"] .jdp-status-badge.awaiting-checkin::before { content: "!"; }
html[data-theme="light"] .jdp-status-badge.booked {
    background: var(--surface-inset);
    border-color: var(--keyline);
    color: var(--ink-dim);
}
html[data-theme="light"] .jdp-status-badge.booked::before { content: "\2022"; }
html[data-theme="light"] .jdp-status-badge.canceled {
    background: var(--surface-inset);
    border-color: var(--keyline-strong);
    color: var(--ink-dim);
}
html[data-theme="light"] .jdp-status-badge.canceled::before { content: "\2715"; }

/* The difficulty badge shares the dot's `::before` but NOT its meaning: `--diff-1..3` is a ramp, not
   a status, and three tiers on a ramp are exactly what a coloured dot is good for. It keeps the dot
   and only loses the capsule it never had. Left alone deliberately, so nobody "completes" the badge
   sweep by flattening a scale that is doing its job. */

/* The count beside a section heading, and the heading itself. A count is not an action. */
html[data-theme="light"] .jdp-count {
    border-radius: 4px;
    padding: 1px 6px;
    font-weight: var(--fw-label);
    font-variant-numeric: tabular-nums;
}
html[data-theme="light"] .jdp-info-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
    text-transform: uppercase;
}
html[data-theme="light"] .jdp-hq-amount,
html[data-theme="light"] .jdp-hq-total,
html[data-theme="light"] .jdp-info-value.mono {
    font-variant-numeric: tabular-nums;
}
/* Secondary controls in the record: surface, structural hairline, ink — and the accent only on the
   press. Section 2 above set their colour; this is the shape. */
html[data-theme="light"] .jdp-full-quote-btn,
html[data-theme="light"] .jdp-supersede-toggle,
html[data-theme="light"] .jdp-quote-more-btn,
html[data-theme="light"] .jdp-routine-btn,
html[data-theme="light"] .jdp-eq-chip {
    border-radius: var(--r-control);
    font-weight: var(--fw-label);
}


/* ── P7. /finished-job-detail — `.fjd-phone` ───────────────────────────────────────────────────
   Every class in this sheet is generic, so every rule stays anchored to the page root.

   `.glass-panel` is the page: emerald-tinted border, 28px blur, a 40px drop shadow and a lit top
   edge. Four dark devices on one element. */
html[data-theme="light"] .fjd-phone .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 sheet declares only `.status-badge.finished`, but this page is reachable for a canceled and a
   collected job too, so the other values render on the bare base — no fill, no border (reported).
   Light gives the base the neutral badge, which means an unmapped value now degrades to a legible
   neutral chip instead of to nothing. */
html[data-theme="light"] .fjd-phone .status-badge {
    border-radius: 4px;
    padding: 2px 6px;
    font-size: 0.6875rem;
    font-weight: var(--fw-label);
    background: var(--surface-inset);
    border: 1px solid var(--keyline);
    color: var(--ink-dim);
    box-shadow: none;
}
html[data-theme="light"] .fjd-phone .status-badge.finished {
    background: var(--st-logged-soft);
    border-color: var(--st-logged-line);
    color: var(--st-logged);
}
html[data-theme="light"] .fjd-phone .status-badge.finished::before { content: "\2713"; }
html[data-theme="light"] .fjd-phone .status-badge.canceled::before { content: "\2715"; }
html[data-theme="light"] .fjd-phone .status-badge.awaiting-pickup::before { content: "!"; }

/* The procedures list is every completed job's whole content, and every row of it is emerald: a
   green tile per row is a colour scheme, not a status. The row goes to the inset rung with a
   keyline; the TICK keeps the green, which is the one place on the row where it says something the
   words do not. */
html[data-theme="light"] .fjd-phone .procedure-item {
    background: var(--surface-inset);
    border-color: var(--keyline);
    border-radius: var(--r-control);
    box-shadow: none;
}
html[data-theme="light"] .fjd-phone .procedure-item::before {
    color: var(--st-logged);
}

html[data-theme="light"] .fjd-phone .info-label {
    font-size: var(--eyebrow-size);
    font-weight: var(--fw-label);
    letter-spacing: var(--eyebrow-track);
    text-transform: uppercase;
}
html[data-theme="light"] .fjd-phone .cost-value,
html[data-theme="light"] .fjd-phone .hq-amount,
html[data-theme="light"] .fjd-phone .hq-total,
html[data-theme="light"] .fjd-phone .info-value.mono {
    font-variant-numeric: tabular-nums;
}
html[data-theme="light"] .fjd-phone .quote-count {
    border-radius: 4px;
    font-weight: var(--fw-label);
    font-variant-numeric: tabular-nums;
}
html[data-theme="light"] .fjd-phone .supersede-toggle,
html[data-theme="light"] .fjd-phone .full-quote-btn,
html[data-theme="light"] .fjd-phone .quote-more-btn {
    border-radius: var(--r-control);
    font-weight: var(--fw-label);
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   STATES THAT VANISH — the Jobs phone board
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   Same defect as the shared components in _base.css, same cause: a hover built on `--glass-bg`,
   which is a 7% white LIFT in dark and resolves to `var(--surface)` — plain white — in light. On a
   white page that is not a weak hover, it is no hover at all.

   The rule these map onto lives in _base.css ("STATES GO DARKER"): in light a state moves DOWN the
   ramp, never toward white. Restated here rather than moved there because these two selectors are
   this cluster's, and a page's class name in the shared file is how a shared file stops being
   shared. */
html[data-theme="light"] .jp-nav-btn:hover,
html[data-theme="light"] .jpa-card.no-detail:hover {
    background: var(--surface-inset);
}


/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   SECTION B  —  the phone shell's remaining touch faults (integrated from the detail cluster)
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   THE OPENING `/*` OF THIS BANNER WAS MISSING FOR ONE INTEGRATION CYCLE, and it is worth recording
   what that cost. Without it the parser read the banner text as a selector and swallowed everything
   after it as that rule's body: phone.css declared 3 `@media` rules in source and the browser's
   CSSOM reported 0, with the last accepted rule at source line 1189 of 1372. **179 lines — 13% of
   the file, and the entire phone touch cluster (§B1 the 36px action stack, §B2 the pencil targets,
   §B3 the context rail, §V1, §V2) — never reached the browser.**

   Nothing catches this. `npm run build` does not parse `public/`, so the build stayed green; the
   rules simply were not there. It was found by an adversarial pass comparing each sheet's source
   `@media` count against `document.styleSheets` in a real browser, which is now the only check that
   would. A missing two characters is worth more attention than it looks: a CSS parse error does not
   fail loudly, it silently deletes everything downstream of itself.
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   phone.css §2 (/job-detail `.jdp-*`) and §3 + §P6 + §P7 (/finished-job-detail `.fjd-phone`) have
   already done the structural and colour pass on both phone twins, and §4/§5 have done /quote-drafts
   and /quote-draft-detail. Nothing below duplicates them. Read as: what the phone shell still fails
   on after those sections have run, measured with the auditor at 412x839.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */


/* ── B1. THE PHONE ACTION STACK IS 36px ────────────────────────────────────────────────────────
   `.btn-astra-glass.jdp-full-btn` x3 — "Checka in", "Hittat mer arbete", "Redigera" — measured
   36x300 each. Same specificity failure as A1, on the shell where it matters most: these are three
   of the five things the phone view exists to do, and they are the only three under the floor
   (`Avboka` and `Markera arbete klart` both measure 56 and are correct).

   `.qdp-msg-toggle` is the same class on /quote-draft-detail's phone editor, measured 36x303.
   CONFIRMED (measured both; rendered after injection). */
@media (pointer: coarse) {
    html[data-theme="light"] .jdp-wrap .btn-astra-glass,
    html[data-theme="light"] .jdp-wrap .btn-astra-neutral,
    html[data-theme="light"] .fjd-phone .btn-astra-glass,
    html[data-theme="light"] .fjd-phone .btn-astra-neutral,
    html[data-theme="light"] .qdp-wrapper .btn-astra-glass,
    html[data-theme="light"] .qdp-wrapper .btn-astra-neutral {
        min-height: var(--tap-min);
    }
}


/* ── B2. THE PENCILS ON THE PHONE ARE STILL THE OPERATING SYSTEM'S ─────────────────────────────
   detail.css §8.2 already diagnosed this and fixed it — `✎` is U+270E, a text-presentation codepoint
   that Windows and Android resolve through the emoji font, so the same character is flat on one
   machine and full-colour on another. That fix is scoped `.detail-wrapper .rsum-pen`, and the phone
   view is `.jdp-wrap`, so it reaches nothing here. Measured on the phone record: two `.rsum-pen`,
   18x44 and 18x16, both rendering the raw glyph.

   Same suppress-and-replace, same token, on the phone root. The 18px width is the second half of the
   same defect — an 18px-wide press target for the control that renames a routine's summary — and the
   mask makes the box explicit rather than letting a glyph's advance width decide it.

   The inline `.static` pencil (inside "Skriv en anteckning", 18x16) is decoration beside a word, not
   a control of its own: it keeps the mask and takes no target, exactly as detail.css §8.2 decided
   for the desktop twin. CONFIRMED (measured; rendered after injection). */
@media (pointer: coarse) {
    html[data-theme="light"] .jdp-wrap .rsum-pen {
        min-width: var(--tap-min);
    }
}

html[data-theme="light"] .jdp-wrap .rsum-pen {
    font-size: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

html[data-theme="light"] .jdp-wrap .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 `.static` one sits inside a labelled button — no target of its own, and a little air. */
html[data-theme="light"] .jdp-wrap .rsum-note-add .rsum-pen.static {
    min-width: 0;
    margin-right: 6px;
}


/* ── B3. THE CONTEXT RAIL IS UNDER THE TAB BAR ON /quote-drafts ────────────────────────────────
   Measured at 412x839: `.crail` runs y=772..1019 inside a `.qdp-wrapper` that is `overflow: auto` at
   839px tall, and the fixed phone tab bar occupies y=782..839. So the rail's first row is 10px clear
   of the bar and everything after it is behind it — the page can be scrolled to the end and the last
   57px of the document is still covered.

   The fix is a bottom reserve on the scroll container equal to the bar, not a change to the rail:
   the rail is a shared component and this is a page-shell problem. 64px = the measured 57px bar plus
   one step of air.

   THIS IS A BOTH-THEMES DEFECT. Dark has the identical geometry (the rail, the bar and the wrapper
   are all theme-invariant), so fixing it light-only leaves the two themes visibly apart on this page
   and leaves dark broken. FOR THE LEAD: it belongs in `QuoteDraftsPhone.css`, not here. It is written
   as a light rule so the touch shell is at least correct in the theme being shipped, and it should be
   DELETED the moment the page sheet takes it. CONFIRMED (measured; rendered after injection). */
@media (pointer: coarse) {
    html[data-theme="light"] .qdp-wrapper {
        padding-bottom: 64px;
    }
}



/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   DESTINATION: public/theme-light/phone.css   — NOT boards.css.  The lead owns this file.
   ═══════════════════════════════════════════════════════════════════════════════════════════════
   Both rules below are on `/vehicle-storage`'s phone stub, whose `.vsp-*` classes phone.css §8
   already owns (phone.css:422). Putting them in boards.css would make `.vsp-badge` a class with two
   owners in two files, which is the drift this whole directory exists to prevent. Append them to
   that same §8 block.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */

/* ── V1. THE ONLY PAGE EYEBROW IN THE APP THAT IS STILL A CHIP ────────────────────────────────
   CONFIRMED — /vehicle-storage at 412x839, THEME=light, compared against the eyebrow on the other
   five routes in this cluster.

   Every page in the app renders its eyebrow as `<span class="badge">`, and `_base.css` gives that
   one treatment: bare lowercase muted text, no fill, no border, `::first-letter` capitalised. This
   view is the exception in the MARKUP — `VehicleStoragePhone.vue:4` uses `.vsp-badge`, its own
   class — so it never matched, and it is the one page where the eyebrow is still a bordered
   uppercase pill reading "FÖRVARING" above a heading that says "Verkstads förvaring".

   phone.css §8 already recoloured it and stopped there: colour was the only thing being audited at
   the time, so the shape survived. This gives it the same SHAPE as every other eyebrow.

   `display: inline-block` is load-bearing, not cosmetic — `::first-letter` needs a block container.
   The case must come from CSS because the string is capitals in the DATA (localization.ts:
   'FÖRVARING'), which `text-transform: none` cannot touch. */
html[data-theme="light"] .vsp-badge {
    display: inline-block;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    color: var(--ink-muted);
    font-size: var(--eyebrow-size);
    letter-spacing: var(--eyebrow-track);
    text-transform: lowercase;
}

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

/* ── V2. THE PAGE SCROLLS SIDEWAYS ON A PHONE ────────────────────────────────────────────────
   CONFIRMED by measurement, and it is a defect in BOTH THEMES that light makes 5x worse.
   Full-page capture width in a 412px viewport:

       dark   418px      6px of horizontal scroll
       light  442px     30px of horizontal scroll

   Trap #14, exactly: `.vsp-stat` is a flex row and `.vsp-stat-value` is `white-space: nowrap`.
   A flex item's automatic minimum size floors at its MIN-CONTENT width, and the min-content width
   of nowrap text is the whole string — so "1 av 9 utrustningsplatser upptagna" cannot shrink, the
   row is wider than its track, and the page grows a horizontal scrollbar. It does not look like an
   overflow; it looks like a chip that runs off the edge of the screen.

   Light is worse because its control grammar puts more horizontal padding on the value chip, so the
   same string needs more room. The underlying fault is the page's, not the theme's — worth fixing
   in BOTH themes at source, and this only removes light's share of it.

   Fixed by letting the value wrap and by letting the flex item shrink. `min-width: 0` is the half
   that actually removes the overflow; `white-space: normal` is what stops the wrap point being the
   whole string. Both are needed.

   The radius comes along in the same rule because it is the same object: a 50px capsule on a
   read-only figure says "press me". Radius ceiling is 4px, and this is a fact, not a control. */
html[data-theme="light"] .vsp-stat {
    flex-wrap: wrap;
    row-gap: 4px;
}

html[data-theme="light"] .vsp-stat-value {
    min-width: 0;
    white-space: normal;
    text-align: right;
    border-radius: var(--r-control);
    font-variant-numeric: tabular-nums;
}


/* ===============================================================================================
   THE PHONE "MER" MENU - the largest floating pale-blue surface left in the app
   ===============================================================================================
   `MoreMenu.css:20` paints `rgba(var(--ch-indigo-06030e), 0.82)` over `backdrop-filter:
   var(--glass-blur)`. In dark that channel is 6,3,14 - near-black glass over the violet ground, and
   it is right. In light the same channel is 226,231,247, so the sheet renders as an 82%-opaque
   PERIWINKLE pane covering the whole phone screen down to the tab bar, with the page showing
   through it as a blue haze. It is the biggest single instance of the flipping-primitive fault
   (trap #3) still shipping, and it is the first thing a phone user sees when they press Mer.

   `--glass-blur` is deliberately NOT touched here. It is not a length - it holds a whole filter
   value at seventeen consumers, and repointing it emits an invalid declaration that is silently
   dropped (trap #1). The blur is harmless once the fill is opaque, because there is nothing left to
   see through.

   The fill goes fully opaque rather than to a light translucent glass. A translucent light surface
   samples white and yields white, so the translucency buys nothing but a legibility risk over
   whatever page happens to be underneath; and this sheet carries body-size navigation text, not the
   10px labels the tab bar carries. `--surface` plus a top hairline where it meets the status bar is
   the whole treatment.

   WHY THIS IS NOT SCOPED TO `[data-shell='phone']`, WHICH IS THE FIRST THING ANYONE WILL TRY.
   It was written that way first and it MATCHED NOTHING - a rendered phone capture still showed the
   periwinkle pane. `MainLayout.vue:14` puts `data-shell` on `<div class="page">`, and line 40 mounts
   `<MoreMenu>` as a SIBLING of that div, not inside it. Same shape as trap #21: the selector parses,
   applies to nothing, and the fix looks shipped. The shell guard has to come from the mount instead,
   and it does - `v-if="shell === 'phone'"` on line 40 means `.more-menu` cannot exist on any other
   shell, and the class is unique to MoreMenu.vue app-wide. So the unscoped rule is exactly as narrow
   as the scoped one intended to be, and unlike it, it works.

   (phone.css loads last, so an unscoped rule here outranks every other light sheet - trap #22. That
   is the reason this note exists rather than the reason to avoid the rule: the element it names only
   ever exists on the phone.)
   =============================================================================================== */
html[data-theme="light"] .more-menu {
    background: var(--surface);
    border-top: 1px solid var(--keyline);
}
