/* ══════════════════════════════════════════════════════════════════════════
   MAIN ADMIN PAGE STYLES (index.html + app.js) — the staff workshop board.

   Rough order of this file: theme variables → page shell/topbar → the job
   board grid and everything on a job tile (bands, badges, notes, WOF
   banners, mechanic blocks + cheerleader) → the Ready For Collection strip →
   modals (PIN pads, pickers, wide Reminders/Reports panels, the dark
   job-items/TLC forms) → footer/empty state → small-screen tweaks.

   Theme note: colours are CSS variables with a prefers-color-scheme dark
   override below, but .dashboard-body pins the board itself to a fixed black
   background — several rules further down exist purely to re-assert readable
   text colours on light-surfaced panels inside that dark page.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Theme variables (light default + dark override) ──────────────────────── */
:root {
  --surface-1:      #fcfcfb;
  --page-plane:      #f9f9f7;
  --text-primary:   #0b0b0b;
  --text-secondary: #52514e;
  --text-muted:     #898781;
  --gridline:       #e1e0d9;
  --border:         rgba(11,11,11,0.10);
  --series-blue:    #2a78d6;
  --series-red:     #e34948;

  --good:      #0ca30c;
  --warning:   #fab219;
  --serious:   #ec835a;
  --critical:  #d03b3b;

  --good-bg:     #e5f6e5;
  --warning-bg:  #fff3d9;
  --critical-bg: #fbe6e6;
  --muted-bg:    #eeede9;
}

@media (prefers-color-scheme: dark) {
  :root {
    --surface-1:      #1a1a19;
    --page-plane:      #0d0d0d;
    --text-primary:   #ffffff;
    --text-secondary: #c3c2b7;
    --text-muted:     #898781;
    --gridline:       #2c2c2a;
    --border:         rgba(255,255,255,0.10);
    --series-blue:    #3987e5;
    --series-red:     #e66767;

    --good:      #0ca30c;
    --warning:   #fab219;
    --serious:   #ec835a;
    --critical:  #d03b3b;

    --good-bg:     #103310;
    --warning-bg:  #3a2e0c;
    --critical-bg: #3a1414;
    --muted-bg:    #262624;
  }
}

/* ── Page shell ───────────────────────────────────────────────────────────── */
* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--page-plane);
  color: var(--text-primary);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Fixed black to match the customer screens' branding, regardless of
   light/dark system preference. Text color is fixed light to match, since
   var(--text-primary) would be near-black in light mode and unreadable here. */
.dashboard-body {
  background: #111111;
  color: #f3f4f6;
}

/* ── Topbar: brand, clock, and Xero/Gmail connection pills ────────────────── */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  padding: 16px 24px;
  background: var(--surface-1);
  /* Stays on the light surface colour regardless of .dashboard-body's forced
     light text colour above — without this, h1 text would be near-invisible
     (light text on this still-light header background). */
  color: var(--text-primary);
  border-bottom: 1px solid var(--border);
}

.topbar h1 {
  font-size: 20px;
  font-weight: 600;
  margin: 0;
}

.topbar .brand {
  display: flex;
  align-items: baseline;
  gap: 14px;
}

.live-clock {
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}

.connections {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.conn {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
}

.conn.ok {
  color: var(--good);
  background: var(--good-bg);
  border-color: transparent;
}

.conn.off {
  color: var(--text-secondary);
  background: var(--surface-1);
}

.conn .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.conn a.connect-btn {
  margin-left: 4px;
  padding: 6px 12px;
  border-radius: 6px;
  background: var(--series-blue);
  color: #fff;
  text-decoration: none;
  font-weight: 600;
  font-size: 13px;
}

.conn a.connect-btn:hover { opacity: 0.9; }

/* ── Rotating weekly background ───────────────────────────────────────────
   Fixed full-viewport layer, negative z-index so it paints below every
   non-positioned element in the normal flow (topbar/board/footer are all
   static, so they — and .job-card's opaque fill — paint on top of this
   automatically; .dashboard-body's own #111111 becomes the page canvas,
   painted furthest back of all, per the usual body→canvas background rule).
   app.js swaps its innerHTML to one of WEEKLY_BACKGROUNDS by ISO week. */
.weekly-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.weekly-bg svg {
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0.2;
}

/* ── Job board grid ───────────────────────────────────────────────────────
   Tile size/gap come from CSS variables that app.js's updateTileSizing()
   sets on the fly so every open job fits on screen without scrolling. */
.board {
  flex: 1;
  padding: 20px 24px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--tile-min, 220px), 1fr));
  grid-auto-rows: var(--tile-row, 170px);
  grid-auto-flow: row;
  gap: var(--tile-gap, 10px);
  align-content: start;
  overflow: hidden; /* safety net only — updateTileSizing() is tuned to avoid ever needing this */
}

/* ── Job tile ─────────────────────────────────────────────────────────────
   One .job-card per open job: job-type band on top, .job-top body (rego,
   vehicle, badges, notes), .job-bottom mechanic strip along the bottom. */
.job-card {
  position: relative;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  user-select: none;
}

.job-card:hover { border-color: var(--text-muted); }

/* Brief "Clock on first" bubble when the main body is tapped with nobody
   clocked on — .job-card already has position:relative/overflow:hidden, so
   this just centres over whatever's currently showing without needing any
   layout changes to job-top itself. */
.tap-message {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.72);
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  text-align: center;
  padding: 12px;
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
  z-index: 5;
}
.tap-message.show { opacity: 1; }

/* Stays until Aljane dismisses it (admin PIN required) — set whenever a
   mechanic submits "What's Gone Into This Job" items to this invoice.
   Deliberately loud: bright lime, pulsing, sits on top of everything else
   on the tile so it reads from across the workshop. */
.review-badge {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 25;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border: none;
  border-radius: 999px;
  background: #ccff00;
  color: #111;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  animation: review-pulse 1.1s ease-in-out infinite;
}

@keyframes review-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.35), 0 4px 14px rgba(204, 255, 0, 0.35); }
  50% { opacity: 0.6; box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.35), 0 4px 26px rgba(204, 255, 0, 0.95); }
}

@media (prefers-reduced-motion: reduce) {
  .review-badge { animation: none; }
  .job-note-badge { animation: none; }
  .wof-recheck-teal, .wof-recheck-orange, .wof-recheck-red { animation: none; }
}

/* Halfway between a normal 1-cell tile and the old 2x2/4-cell size — still
   clearly bigger than an unassigned job, not as oversized as before. Full
   row-height is kept (rather than halving height instead of width) so
   job-bottom has room for the cheerleader without any risk of it silently
   clipping against this card's own overflow:hidden. */
.job-card.tile-large { grid-column: span 1; grid-row: span 2; }

.job-type-band {
  height: 22px;
  flex: none;
  width: 100%;
  display: flex;
  overflow: hidden;
  /* Rainbow-arc bottom edge: the two bottom corners' horizontal radii (50%
     each) meet at the card's centre, forming one continuous curve across
     the full width. Top-right gets an explicit rounded corner; top-left is
     square since it's flush against the card's own rounded top-left edge
     (already clipped by .job-card's overflow:hidden). Segments (below) are
     clipped to this same arc shape via this element's own overflow:hidden. */
  border-radius: 0 14px 50% 50% / 0 14px 70% 70%;
  box-shadow: inset 0 -3px 6px rgba(0, 0, 0, 0.2);
}

.job-type-segment {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.job-type-badge {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  color: #111;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: calc(100% - 6px);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}

/* AUTHORISED tiles only (see jobTypeBandHtml in app.js) — replaces the
   job-type colour band with a single mid-green "AWAITING COLLECTION"
   segment. Deliberately a lighter/brighter green than the tile's own
   .status-ready fill (#2D6A4F) so the band reads as a distinct accent
   rather than blending into the dark-green body below it. */
.job-type-segment.awaiting-collection-segment { background: #43A047; }
.awaiting-collection-segment .job-type-badge { background: rgba(255, 255, 255, 0.92); color: #111; }

.job-top {
  position: relative;
  flex: 1;
  min-height: 0;
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: hidden;
}

/* Parts-supplier quick-launch links — small and unobtrusive in the top
   corner, sitting inside .job-top (not the whole .job-card) so they land
   below the review badge / WOF flash banner in flow rather than overlapping
   them; 22px icons 4px from the edge stay well within .job-top's own box,
   so its overflow:hidden (needed elsewhere to clip long content) never
   clips them. */
.parts-links {
  position: absolute;
  top: 4px;
  right: 4px;
  display: flex;
  gap: 4px;
  /* Above .review-badge's z-index:25 — that badge is centred and can be
     wide enough (long text, white-space:nowrap) to reach into this corner
     on a narrow tile, especially on an AUTHORISED job with no job-type-band
     pushing .job-top down. Keeping these on top means they stay fully
     visible/clickable even on the rare tile where both show at once. */
  z-index: 30;
}

.parts-link {
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  background: #111111;
  font-size: 12px;
  line-height: 1;
  text-decoration: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  transition: transform 0.12s ease;
}

.parts-link:hover, .parts-link:focus-visible { transform: scale(1.12); }

.parts-autodata  { border: 1.5px solid #6b7280; }

/* Always visible regardless of clock status — the tile's main-body tap
   already opens the job-completion form (or a "clock on first" message),
   so notes get their own clearly separate affordance rather than
   overloading that tap (see openJobFormFromMainBody). Mirrors .parts-links'
   own top-corner placement/z-index, opposite corner so neither crowds the
   other. */
.add-note-btn {
  position: absolute;
  top: 4px;
  left: 4px;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 3px 7px;
  border: none;
  border-radius: 999px;
  background: #111111;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  cursor: pointer;
  transition: transform 0.12s ease;
}
.add-note-btn:hover, .add-note-btn:focus-visible { transform: scale(1.08); }

/* Tile body colour keyed to the job's current status (set by app.js) */
.job-top.status-progress { background: #90EE90; color: #111; }
.job-top.status-ready    { background: #2D6A4F; color: #fff; }
.job-top.status-fail     { background: var(--critical); color: #fff; }
.job-top.status-pass     { background: #4A90B8; color: #fff; }

/* ── WOF tile age shading (see wofAgingClasses in app.js) ─────────────────
   WOF/recheck tiles waiting on a result step through these 5 pinks as the
   job ages; the two darkest flip to white text. Only ever combined with
   .status-progress — FAIL/PASS/Ready colours above always win because
   app.js never emits .wof-age alongside them. Each shade also carries its
   own one-step-darker var so the recheck pulse (below) knows where to fade
   to without a per-shade keyframe. */
.job-top.status-progress.wof-age   { background: var(--wof-age-bg); }
.job-top.status-progress.wof-age-0 { --wof-age-bg: #FFE1EE; --wof-age-darker: #FFB8D6; }
.job-top.status-progress.wof-age-1 { --wof-age-bg: #FFB8D6; --wof-age-darker: #FF87BC; }
.job-top.status-progress.wof-age-2 { --wof-age-bg: #FF87BC; --wof-age-darker: #E9569E; }
.job-top.status-progress.wof-age-3 { --wof-age-bg: #E9569E; --wof-age-darker: #C2247A; color: #fff; }
.job-top.status-progress.wof-age-4 { --wof-age-bg: #C2247A; --wof-age-darker: #A31563; color: #fff; }

/* Recheck tiles only: gentle ~2s fade between the current shade and one
   step darker — noticeable urgency without a hard blink, so it never
   competes with the PASS/FAIL flash banners or note badges. */
.job-top.status-progress.wof-age-pulse {
  animation: wof-age-pulse 2s ease-in-out infinite;
}
@keyframes wof-age-pulse {
  0%, 100% { background-color: var(--wof-age-bg); }
  50%      { background-color: var(--wof-age-darker); }
}

/* TLC tiles — same 30-min-step-darker schedule as plain WOF, yellow instead
   of pink; no pulse (only WOF Recheck flashes). Self-contained `background`
   (not the shared --wof-age-bg var) so it's never accidentally overridden by
   the WOF rule above — see wofAgingClasses in app.js, which only ever
   applies .tlc-age alongside .wof-age for genuine WOF/recheck tiles, never
   TLC-only or combined ones. */
.job-top.status-progress.tlc-age-0 { background: #FFF9C4; }
.job-top.status-progress.tlc-age-1 { background: #FFF176; }
.job-top.status-progress.tlc-age-2 { background: #FDD835; }
.job-top.status-progress.tlc-age-3 { background: #F9A825; color: #fff; }
.job-top.status-progress.tlc-age-4 { background: #E65100; color: #fff; }

/* Combined "WOF & TLC" tiles — vertical split, pink half / yellow half, one
   shade index driving both halves together (see wofAgingClasses). Colours
   match the solo WOF/TLC ramps above exactly, shade-for-shade. */
.job-top.status-progress.wof-tlc-split-0 { background: linear-gradient(to right, #FFE1EE 50%, #FFF9C4 50%); }
.job-top.status-progress.wof-tlc-split-1 { background: linear-gradient(to right, #FFB8D6 50%, #FFF176 50%); }
.job-top.status-progress.wof-tlc-split-2 { background: linear-gradient(to right, #FF87BC 50%, #FDD835 50%); }
.job-top.status-progress.wof-tlc-split-3 { background: linear-gradient(to right, #E9569E 50%, #F9A825 50%); color: #fff; }
.job-top.status-progress.wof-tlc-split-4 { background: linear-gradient(to right, #C2247A 50%, #E65100 50%); color: #fff; }

/* Every other job type (service/brakes/tyres/alignment/diag/etc.) — same
   30-min-step schedule as WOF/TLC above, but a green ramp deliberately
   capped lighter than .status-ready's #2D6A4F: #4CAF50 (shade 4, the
   darkest this ramp reaches) is still clearly brighter/lighter than that
   approved dark green, so no amount of aging on a still-working job can
   read as "approved" at a glance. */
.job-top.status-progress.other-age-0 { background: #C8E6C9; }
.job-top.status-progress.other-age-1 { background: #A5D6A7; }
.job-top.status-progress.other-age-2 { background: #81C784; }
.job-top.status-progress.other-age-3 { background: #66BB6A; color: #fff; }
.job-top.status-progress.other-age-4 { background: #4CAF50; color: #fff; }

/* ── Ready For Collection strip ───────────────────────────────────────────
   Separate from the main grid entirely (see isInCollectionRow in app.js) —
   a fixed-height horizontally-scrolling row so a full day's completed jobs
   never wrap into (or squeeze) the main board's tile space above it. */
.collection-row {
  flex: none;
  padding: 10px 24px 14px;
  background: var(--surface-1);
  border-top: 1px solid var(--border);
}

.collection-row-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.collection-row-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

.collection-row-label {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-primary);
  white-space: nowrap;
}

.collection-row-strip {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  overflow-y: hidden;
  padding-bottom: 4px;
}

/* Deliberately smaller than a main-grid tile (.job-card) — compact enough
   that a whole day's worth fit on screen at once, display-only (no tap
   action, unlike the main grid's clock-in/job-form tiles). */
.collection-tile {
  flex: none;
  width: 132px;
  padding: 8px 10px;
  border-radius: 10px;
  border: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.collection-tile .ct-rego {
  font-weight: 800;
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.collection-tile .ct-vehicle {
  font-size: 11px;
  opacity: 0.85;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.collection-tile .ct-customer {
  font-size: 11px;
  opacity: 0.85;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.collection-tile .ct-status {
  margin-top: 3px;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* Plain "done" — no WOF involved (or the WOF isn't the whole story), same
   dark-green already used for status-ready elsewhere on this board. */
.collection-tile.state-ready { background: #2D6A4F; color: #fff; }
/* Brighter, more saturated green than state-ready — a WOF pass is worth
   calling out specifically, not just blending into "done". */
.collection-tile.state-pass  { background: #00C853; color: #111; }
.collection-tile.state-fail  { background: var(--critical); color: #fff; }

/* ── Job tile badges (status / new customer / WOF details) ────────────────── */
.status-badge {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(127, 127, 127, 0.25);
  color: inherit;
}

/* Purely informational (no tap action, unlike .review-badge) — sits inline
   in the card's own top-to-bottom flow rather than absolutely positioned,
   so it never fights .review-badge/.parts-links for the same corner. */
.new-customer-badge {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: 999px;
  background: #9ACD32;
  color: #111111;
}

/* Draft job, "REGO - Make Model" contact, no email on file (see
   noEmailOnFile / addAcquireEmailLineItem in server.js) — same in-flow pill
   pattern as .new-customer-badge, orange so it reads as a to-do rather than
   an FYI. */
.no-email-badge {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: 999px;
  background: #F9A825;
  color: #111111;
}

/* "Days since original WOF" — recheck tiles only (see hasRecheckCode/
   wofRecheckDays/wofRecheckFound, set server-side). Same in-flow pill
   pattern as .new-customer-badge; teal/orange/red flash faster as the
   28-day recheck window closes, reusing mech-pulse (see .job-note-badge)
   rather than a new keyframe. */
.wof-recheck-badge {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-radius: 999px;
  color: #fff;
}
.wof-recheck-teal   { background: #00838F; animation: mech-pulse 1.3s ease-in-out infinite; }
.wof-recheck-orange { background: #E65100; animation: mech-pulse 1s ease-in-out infinite; }
.wof-recheck-red    { background: #C62828; animation: mech-pulse 0.6s ease-in-out infinite; }
.wof-recheck-none {
  background: rgba(127, 127, 127, 0.25);
  color: inherit;
  font-weight: 700;
}

/* WOF Fail reasons — kept small/compact (capped at 4 in the JS, "+N more"
   beyond that) so it fits the card's existing space rather than risking the
   silent overflow:hidden clipping .job-top already has for other content. */
.wof-fail-reasons {
  margin: 2px 0 0;
  padding-left: 14px;
  font-size: 10px;
  font-weight: 600;
  line-height: 1.3;
}
.wof-fail-reasons li { margin: 0; }
.wof-fail-reasons .wof-fail-more { font-weight: 500; font-style: italic; opacity: 0.85; list-style: none; margin-left: -14px; }

/* Job note badge — open to any staff member (see the "+ Note" tile button
   and the job-items form's own note field), flashes in the author's own
   personal colour (--author-color/--author-text set inline per note, reused
   from MECHANICS in server.js — same colour as their clock-in block).
   Persists with the job regardless of staffing. Sits above the rego/vehicle
   head, in-flow rather than absolutely positioned like .review-badge, so
   more than one note can stack without overlapping. A smooth ~1/s opacity
   pulse (mech-pulse, already used for the pending mechanic block) — never a
   hard strobe. */
.job-note-badge {
  margin: 0 0 4px;
  padding: 4px 8px;
  border-radius: 8px;
  background: var(--author-color, #E91E8C);
  color: var(--author-text, #fff);
  border: 1px solid var(--author-border, transparent);
  cursor: pointer;
  animation: mech-pulse 1s ease-in-out infinite;
}
/* Added automatically when the author's colour is too dull/dark to read
   clearly while flashing (see relativeLuminance in app.js) — a bright
   fluoro ring on top of the pulse, computed from the actual colour rather
   than a hardcoded list of who needs one. */
.job-note-badge.needs-outline {
  animation: note-pulse-outline 1s ease-in-out infinite;
}
/* Aljane's white-base + accent-ring treatment (see .mech-block.mech-accent
   above — same idea, same --author-accent-shadow set inline per note). */
.job-note-badge.accent {
  border-color: transparent;
  box-shadow: var(--author-accent-shadow);
}
.job-note-badge-head {
  display: flex;
  align-items: center;
  gap: 6px;
}
.job-note-badge-author {
  flex: none;
  font-size: 11px;
  font-weight: 800;
}
.job-note-badge-time {
  flex: none;
  font-size: 10px;
  font-weight: 600;
  opacity: 0.85;
}
.job-note-badge-text {
  flex: 1;
  min-width: 0;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.job-note-badge.expanded .job-note-badge-text { white-space: normal; }

/* Fluoro green ring, pulsing in sync with the badge's own opacity — bright
   enough to grab attention even for a dull/dark author colour, smooth
   rather than a hard strobe (~1 cycle/second, matching mech-pulse). */
@keyframes note-pulse-outline {
  0%, 100% { opacity: 1;    box-shadow: 0 0 0 2px #39FF14, 0 0 6px 2px rgba(57, 255, 20, 0.6); }
  50%      { opacity: 0.75; box-shadow: 0 0 0 2px #39FF14, 0 0 14px 5px rgba(57, 255, 20, 0.95); }
}
/* Placed after .needs-outline's own rule above (not the shared
   prefers-reduced-motion block near the top of this file) so it wins the
   cascade — equal specificity to that rule, so source order decides, and an
   earlier reduced-motion block would otherwise lose to this file's later
   .needs-outline definition. Same trap as .modal-overlay[hidden] elsewhere. */
@media (prefers-reduced-motion: reduce) {
  .job-note-badge.needs-outline { animation: none; }
}

/* Admin-only dismiss — tapping opens a PIN-gated confirm (see
   openNoteDismissModal in app.js), same admin-PIN pattern as the review
   badge's own dismiss flow. */
.job-note-dismiss {
  flex: none;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.25);
  color: #fff;
  font-size: 10px;
  font-weight: 800;
  line-height: 1;
  cursor: pointer;
}
.job-note-dismiss:hover, .job-note-dismiss:focus-visible { background: rgba(255, 255, 255, 0.45); }
.job-note-full {
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px solid rgba(255, 255, 255, 0.4);
  font-size: 11px;
  font-weight: 500;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Big flashing WOF PASSED/FAILED banner — shown for 2 minutes right after
   an ItsAllAuto result email lands (see wofFlashActive in app.js), first
   thing on the card so it's the most obvious element on the whole tile.
   Settles back to just the normal .job-top.status-pass/status-fail colour
   once app.js stops rendering it (re-checked every 30s poll, no separate
   client-side timer needed). */
.wof-flash-banner {
  flex: none;
  width: 100%;
  padding: 10px 12px;
  font-size: 15px;
  font-weight: 800;
  text-align: center;
  color: #fff;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  line-height: 1.3;
}

.wof-flash-pass { background: #00C853; animation: wof-flash-glow-green 0.8s ease-in-out infinite; }
.wof-flash-fail { background: #D32F2F; animation: wof-flash-glow-red 0.8s ease-in-out infinite; }

@keyframes wof-flash-glow-green {
  0%, 100% { background: #00C853; box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.3); }
  50%      { background: #00E676; box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.9), 0 0 22px 4px rgba(0, 230, 118, 0.8); }
}

@keyframes wof-flash-glow-red {
  0%, 100% { background: #D32F2F; box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.3); }
  50%      { background: #FF1744; box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.9), 0 0 22px 4px rgba(255, 23, 68, 0.8); }
}

/* Board-level system/diagnostic warning banners (Gmail down, Xero quota,
   labour-write failures, invalid item codes, usage pace/spike) removed on
   21 Jul 2026 ("the board is for jobs only") — see .system-health-* styles
   further down for their owner-only Reporting replacement. wof-flash-glow-red
   above is still used elsewhere (job tiles, Today's Bookings row), untouched. */

/* ── TODAY'S BOOKINGS strip (main dashboard) ──────────────────────────────
   Own background/border — visually distinct from job tiles — sitting below
   the nav tabs, above the board (see #todaysBookingsStrip in index.html).
   Populated by renderTodaysBookingsStrip in app.js from data.todaysBookings
   on the existing /api/jobs poll. */
.todays-bookings-strip {
  margin: 0 24px 16px;
  padding: 12px 16px;
  border-radius: 12px;
  background: var(--warning-bg);
  border: 1px solid var(--warning);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}
.todays-bookings-strip[hidden] { display: none; }
.tb-heading { font-weight: 800; font-size: 13px; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.04em; flex: none; }

.tb-row {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 10px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
}
.tb-row:hover { border-color: var(--text-muted); }
.tb-row-time { font-weight: 800; }
.tb-row-type {
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 800;
  color: #111;
}
.tb-row-type-wof { background: #FF69B4; }
.tb-row-type-tlc { background: #FFD700; }
.tb-row-rego { font-weight: 800; }
.tb-row-name { color: var(--text-muted); font-weight: 500; }
.tb-row-countdown { font-size: 11px; color: var(--warning); font-weight: 700; }

/* 15-minutes-before-slot flash — re-evaluated fresh each poll (same
   "good enough on next poll" approach as .wof-flash-pass/fail above), no
   separate client-side ticking timer needed. */
.tb-row-flash { animation: wof-flash-glow-red 1.2s ease-in-out infinite; color: #fff; }
.tb-row-flash .tb-row-name, .tb-row-flash .tb-row-countdown { color: #fff; }

.tb-tomorrow-note {
  margin-left: auto;
  font-size: 12px;
  color: var(--text-muted);
  font-style: italic;
}

/* ── Booking status badges (strip rows, admin list, detail modal) ────────── */
.booking-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.booking-badge-tentative { background: var(--warning-bg); color: var(--warning); }
.booking-badge-confirmed { background: var(--good-bg); color: var(--good); }
.booking-badge-lapsed, .booking-badge-cancelled { background: var(--muted-bg); color: var(--text-muted); }

/* ── Admin urgency cards (Bookings tab) — the amber/red escalation ladder.
   Reuses wof-flash-glow-red (defined above) for both red tiers, varying
   animation-duration for "faster" the same way .wof-recheck-teal/orange/red
   do further down this file, rather than a second keyframe. ──────────────── */
.booking-card {
  border-radius: 12px;
  border: 1px solid var(--border);
  padding: 14px 16px;
  margin-bottom: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.booking-card-flash-amber {
  background: var(--warning);
  color: #111;
  animation: booking-card-pulse-amber 1.6s ease-in-out infinite;
}
.booking-card-escalated,
.booking-card-lapsing-soon,
.booking-card-capacity-conflict {
  animation: wof-flash-glow-red 0.5s ease-in-out infinite;
  color: #fff;
}
.booking-card-steady-amber {
  background: var(--warning-bg);
  border-color: var(--warning);
}
.booking-card-confirmed-flash {
  animation: wof-flash-glow-green 0.7s ease-in-out 3;
  color: #fff;
}
.booking-card-lapsed, .booking-card-cancelled {
  background: var(--muted-bg);
  color: var(--text-muted);
}
@keyframes booking-card-pulse-amber {
  0%, 100% { background: var(--warning); box-shadow: inset 0 0 0 2px rgba(255,255,255,0.25); }
  50%      { background: #ffcc4d; box-shadow: inset 0 0 0 2px rgba(255,255,255,0.7); }
}
.booking-card-title { font-weight: 800; font-size: 15px; }
.booking-card-sub { font-size: 12px; opacity: 0.85; }
.booking-card-countdowns { display: flex; gap: 14px; font-size: 12px; font-weight: 700; }
.booking-card-mobile { font-size: 20px; font-weight: 800; }
.booking-card-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.booking-card-actions button {
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,0.5);
  background: rgba(255,255,255,0.15);
  color: inherit;
  font-weight: 700;
  cursor: pointer;
  font-size: 12px;
}
.booking-card-steady-amber .booking-card-actions button,
.booking-card-lapsed .booking-card-actions button,
.booking-card-cancelled .booking-card-actions button {
  border-color: var(--border);
  background: var(--surface-1);
  color: var(--text-primary);
}

/* ── Bookings admin modal (list, closures editor, new-booking form) ──────── */
.bookings-toolbar { padding: 12px 20px 0; }
.modal-btn-primary {
  padding: 10px 18px;
  border-radius: 8px;
  border: none;
  background: var(--series-blue);
  color: #fff;
  font-weight: 700;
  cursor: pointer;
  font-size: 14px;
}
.bookings-closures { margin: 12px 20px 0; padding: 10px 14px; border: 1px solid var(--border); border-radius: 10px; }
.bookings-closures summary { cursor: pointer; font-weight: 700; }
.bookings-closures-body { display: flex; flex-direction: column; gap: 6px; margin: 10px 0; max-height: 180px; overflow-y: auto; }
.bookings-closure-row { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.bookings-closure-row button { border: none; background: var(--critical-bg); color: var(--critical); border-radius: 6px; width: 22px; height: 22px; cursor: pointer; font-weight: 800; }
.bookings-closures-add { display: flex; gap: 8px; flex-wrap: wrap; }
.bookings-closures-add input { padding: 6px 8px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface-1); color: var(--text-primary); }

.bookings-body { flex: 1; overflow-y: auto; padding: 12px 20px 24px; }
.bookings-day-heading { font-weight: 800; font-size: 15px; margin: 16px 0 8px; }
.bookings-day-heading:first-child { margin-top: 0; }

.bookings-over-capacity {
  margin: 0 20px;
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--critical-bg);
  color: var(--critical);
  font-weight: 700;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.bk-admin-form { display: flex; flex-direction: column; gap: 10px; }
.bk-admin-form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; font-weight: 600; color: var(--text-secondary); }
.bk-admin-form input, .bk-admin-form select {
  font-size: 15px; padding: 10px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--page-plane); color: var(--text-primary);
}

.bookings-detail-body { display: flex; flex-direction: column; gap: 8px; font-size: 14px; }
.bookings-detail-row { display: flex; justify-content: space-between; gap: 10px; }
.bookings-detail-row span:first-child { color: var(--text-muted); }

@media (prefers-reduced-motion: reduce) {
  /* These must come AFTER the animation rules they disable — an earlier
     same-specificity `animation: none` loses the cascade to a later
     `animation: ...` (media queries add no priority), which is why these
     aren't in the reduce block near the top of the file. The aged recheck
     tile just holds its current static shade. */
  .wof-flash-pass, .wof-flash-fail { animation: none; }
  .job-top.status-progress.wof-age-pulse { animation: none; }
  .tb-row-flash { animation: none; }
  .nav-tab-badge.severity-red { animation: none; }
  .booking-card-flash-amber,
  .booking-card-escalated,
  .booking-card-lapsing-soon,
  .booking-card-capacity-conflict,
  .booking-card-confirmed-flash { animation: none; }
}

.wof-flash-reasons {
  margin: 6px 0 0;
  padding-left: 18px;
  text-align: left;
  font-size: 12px;
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  line-height: 1.3;
}

/* ── Job tile text content: rego, vehicle, account, line items, meta row ── */
.job-top .job-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}

.job-top .rego {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.job-top .rego.rego-fallback {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: normal;
}

.job-top .vehicle {
  font-size: 14px;
  opacity: 0.9;
  margin-top: -2px;
}

.job-top .account {
  font-size: 12px;
  opacity: 0.75;
}

.job-top .line-items {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-top: 2px;
}

.job-top .line-item {
  font-size: 12px;
  opacity: 0.85;
  line-height: 1.3;
}

.job-top .meta-row {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  opacity: 0.75;
  margin-top: auto;
  padding-top: 4px;
  border-top: 1px solid rgba(127, 127, 127, 0.3);
}

/* ── Mechanic strip (.job-bottom) + cheerleader ───────────────────────────
   The bottom of every tile: one .mech-block per mechanic slot, tinted with
   that mechanic's personal colour (--mech-color/--mech-text set inline by
   app.js). An actively clocked-on mechanic's block also gets the animated
   cheerleader figure and its speech bubble. */
.job-bottom {
  display: flex;
  min-height: 32px;
}

/* Taller (not wider — the tile itself is now 1 column instead of 2, see
   .job-card.tile-large) to fit the cheerleader stacked below the mechanic's
   name/timer; wraps so 2-3 simultaneous mechanics don't get squeezed into
   an unreadable sliver at the narrower width. */
.job-card.tile-large .job-bottom { min-height: 110px; flex-wrap: wrap; }

.mech-block {
  flex: 1 1 70px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 4px 6px;
  font-size: 12px;
  font-weight: 600;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  background: var(--mech-color, var(--muted-bg));
  color: var(--mech-text, var(--text-muted));
  border: 1px solid var(--mech-border, transparent);
}

.mech-block + .mech-block { border-left: 1px solid rgba(127, 127, 127, 0.25); }

.mech-block.empty {
  background: var(--muted-bg);
  color: var(--text-muted);
  font-weight: 500;
  font-style: italic;
}

.mech-block.pending { animation: mech-pulse 1.2s ease-in-out infinite; }

/* Permanent striped mechanics (Mauricio/Kayne/Aljane — see stripeBackground
   on their MECHANICS entry in server.js) — one shared class, the actual
   gradient/hoop pattern comes from --mech-stripe-bg set inline per mechanic
   (mechBlockHtml); --mech-color itself stays a plain solid colour so the
   clock-off-reminder glow (box-shadow, which can't take a gradient) still
   gets a valid value. */
.mech-block.mech-stripe {
  background: var(--mech-stripe-bg);
}

/* Aljane's white-base + Philippines-flag accent-ring treatment (see her
   MECHANICS entry, accentBorder) — box-shadow inset rings instead of a
   border, so the block's own rounded corners stay intact (border-image
   doesn't respect border-radius; box-shadow does). --mech-accent-shadow is
   set inline per mechanic (mechBlockHtml). */
.mech-block.mech-accent {
  border-color: transparent;
  box-shadow: var(--mech-accent-shadow);
}

/* Active mechanic blocks stack their info line above the cheerleader
   instead of the single centred row every other block uses — overflow
   resets to visible/normal here so the cheerleader (which extends below
   the info line) isn't clipped by the row-layout rules above. */
.mech-block.active {
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 6px 4px;
  overflow: visible;
  white-space: normal;
}

.mech-info-row {
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.cheer-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.cheer-bubble {
  position: relative;
  background: #fff;
  color: #111;
  font-size: 9px;
  font-weight: 800;
  padding: 2px 7px;
  border-radius: 999px;
  white-space: nowrap;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  animation: cheer-bubble-bounce 1s ease-in-out infinite;
}

.cheer-bubble::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -4px;
  transform: translateX(-50%);
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 4px solid #fff;
}

.cheer-svg { width: 34px; height: 39px; overflow: visible; flex: none; }

.cheer-figure  { animation: cheer-bounce 0.5s ease-in-out infinite; transform-origin: 19px 43px; }
.cheer-arm-l   { animation: cheer-wave-l 0.5s ease-in-out infinite; transform-origin: 13px 18px; }
.cheer-arm-r   { animation: cheer-wave-r 0.5s ease-in-out infinite; transform-origin: 26px 18px; }
.cheer-pompom-anim { animation: cheer-pompom-shake 0.25s ease-in-out infinite; }

@keyframes cheer-bubble-bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
@keyframes cheer-bounce   { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-3px) rotate(-3deg); } }
@keyframes cheer-wave-l   { 0%, 100% { transform: rotate(-25deg); } 50% { transform: rotate(25deg); } }
@keyframes cheer-wave-r   { 0%, 100% { transform: rotate(25deg); }  50% { transform: rotate(-25deg); } }
@keyframes cheer-pompom-shake { 0%, 100% { transform: rotate(-20deg) scale(1); } 50% { transform: rotate(20deg) scale(1.15); } }

.mech-timer { font-variant-numeric: tabular-nums; opacity: 0.85; }

/* Shown once a mechanic's been clocked on over an hour — purely a visual
   nudge (see updateAllTimers() in app.js), never clocks anyone off. The
   block's own background is already that mechanic's colour, so the badge
   itself stays a legible white pill and "flashes in their colour" via a
   pulsing glow instead of a flat fill (which would just vanish into the
   block behind it). ".urgent" (2hrs+) pulses noticeably faster. */
.clock-off-reminder:not([hidden]) {
  display: inline-flex;
  align-items: center;
  flex: none;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.95);
  color: #111;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  white-space: nowrap;
  animation: clock-off-pulse 1.4s ease-in-out infinite;
}
.clock-off-reminder[hidden] { display: none; }
.clock-off-reminder.urgent { animation-duration: 0.6s; }

.icon-spanner, .icon-bolt, .icon-hourglass, .icon-happy {
  display: inline-block;
  line-height: 1;
}

.icon-spanner { animation: mech-spin 1.6s linear infinite; }
.icon-happy   { animation: mech-bounce 1s ease-in-out infinite; }

@keyframes mech-spin   { to { transform: rotate(360deg); } }
@keyframes mech-pulse  { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
@keyframes mech-bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }
@keyframes clock-off-pulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--mech-color, #fff), 0 0 4px 1px var(--mech-color, transparent); opacity: 1; }
  50%      { box-shadow: 0 0 0 2px var(--mech-color, #fff), 0 0 14px 5px var(--mech-color, transparent); opacity: 0.7; }
}

@media (prefers-reduced-motion: reduce) {
  .icon-spanner, .icon-happy, .mech-block.pending, .clock-off-reminder { animation: none; }
  .cheer-bubble, .cheer-figure, .cheer-arm-l, .cheer-arm-r, .cheer-pompom-anim { animation: none; }
}

/* ── Modals (PIN pad + Aljane mechanic picker) ──────────────────────────── */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
  padding: 16px;
}

.modal-overlay[hidden] { display: none; }

.modal-panel {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 20px;
  width: min(320px, 100%);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Without this, h2 inherits .dashboard-body's near-white text colour against
   this panel's light surface background and reads as nearly invisible —
   pre-existing on every PIN modal, not just the new Reports gate. */
.modal-panel h2 { margin: 0; font-size: 16px; text-align: center; color: var(--text-primary); }
.modal-panel .modal-sub { font-size: 13px; color: var(--text-secondary); text-align: center; margin-top: -10px; }

/* Opaque, above every other modal — hides the board entirely until the
   shared staff PIN is entered, both on load and after 5 minutes idle. */
.app-lock-overlay {
  position: fixed;
  inset: 0;
  background: var(--page-plane);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 16px;
}

.app-lock-overlay[hidden] { display: none; }

.app-lock-panel { width: min(360px, 100%); gap: 18px; }

/* Styling-only cue for an active per-IP lockout (see auth.js) — the keypad
   stays fully usable underneath (the master unlock code must keep working
   during a lockout), this just signals "something's locked" at a glance. */
.app-lock-overlay.locked-out .app-lock-panel { border: 2px solid var(--critical); }

.app-lock-title { margin: 0; font-size: 26px; font-weight: 700; text-align: center; }

/* PIN dots + numeric keypad — shared by the app lock and every PIN modal */
.pin-dots {
  display: flex;
  gap: 12px;
  justify-content: center;
}

.pin-dots .pin-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid var(--border);
  background: transparent;
}

.pin-dots .pin-dot.filled { background: var(--text-primary); border-color: var(--text-primary); }

.keypad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.keypad button {
  padding: 16px 0;
  font-size: 20px;
  font-weight: 600;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--page-plane);
  color: var(--text-primary);
  cursor: pointer;
}

.keypad button:active { background: var(--gridline); }

.modal-error {
  color: var(--critical);
  font-size: 13px;
  text-align: center;
  min-height: 16px;
}

.mechanic-picker-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 320px;
  overflow-y: auto;
}

.mechanic-picker-list button {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  border: none;
  cursor: pointer;
  font-weight: 600;
  font-size: 14px;
}

.mechanic-picker-list button:disabled { opacity: 0.4; cursor: default; }

/* ── Clock-off choice screen ──────────────────────────────────────────────── */
.clock-off-choice-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.clock-off-choice-btn {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px;
  border-radius: 12px;
  border: 2px solid var(--border);
  background: var(--page-plane);
  color: var(--text-primary);
  cursor: pointer;
  text-align: left;
}

.clock-off-choice-btn.primary {
  border-color: var(--good);
  background: var(--good-bg);
}

.choice-icon { font-size: 28px; flex: none; line-height: 1; }

.choice-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.choice-title { font-size: 16px; font-weight: 700; }
.choice-desc { font-size: 13px; color: var(--text-secondary); }

.modal-cancel {
  align-self: center;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 13px;
  cursor: pointer;
  text-decoration: underline;
}

/* ── Topbar Reminders/Reports buttons ─────────────────────────────────────── */
.topbar-actions { display: flex; gap: 10px; }

.topbar-btn {
  padding: 8px 14px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface-1);
  color: var(--text-primary);
  cursor: pointer;
}
.topbar-btn:hover { border-color: var(--text-muted); }

.topbar-badge-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: 6px;
  border-radius: 999px;
  background: #d03b3b;
  color: #fff;
  font-size: 11px;
  font-weight: 800;
}

/* ── NAV TABS — Reporting/Reminders/Bookings, deliberately much bigger than
   .topbar-btn above (touchscreen use) ────────────────────────────────────── */
.nav-tabs {
  display: flex;
  gap: 12px;
  padding: 0 24px 16px;
  flex-wrap: wrap;
}
.nav-tab {
  position: relative;
  padding: 14px 22px;
  min-height: 52px;
  font-size: 17px;
  font-weight: 700;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--surface-1);
  color: var(--text-primary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.nav-tab:hover { border-color: var(--text-muted); }
.nav-tab:active { transform: scale(0.98); }

.nav-tab-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 800;
  background: var(--warning);
  color: #111;
}
.nav-tab-badge.severity-red {
  background: var(--critical);
  color: #fff;
  animation: nav-badge-flash 1s ease-in-out infinite;
}
@keyframes nav-badge-flash { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }

@media (max-width: 720px) {
  .nav-tabs { padding: 0 12px 12px; gap: 8px; }
  .nav-tab { padding: 12px 16px; font-size: 15px; min-height: 46px; }
}

/* ── Check-ins list (reuses .reminders-body's scroll/spacing) ────────────── */
.checkin-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-1);
}
.checkin-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.checkin-row-rego { font-weight: 800; font-size: 15px; }
.checkin-row-name { font-size: 13px; color: var(--text-primary); }
.checkin-row-meta { font-size: 12px; color: var(--text-muted); }
.checkin-flag {
  align-self: flex-start;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 2px 8px;
  border-radius: 999px;
}
.checkin-flag-reception { background: rgba(233, 30, 140, 0.16); color: #E91E8C; }
.checkin-flag-review { background: rgba(255, 152, 0, 0.18); color: #B45300; }
.checkin-dismiss-btn {
  flex: none;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 8px;
  background: rgba(211, 47, 47, 0.12);
  color: #d32f2f;
  font-weight: 800;
  cursor: pointer;
}

/* ── Job tile check-in channel badge ──────────────────────────────────────── */
.checkin-channel-badge {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(127, 127, 127, 0.25);
  color: inherit;
}
.checkin-channel-badge.needs-review { background: rgba(255, 152, 0, 0.3); }

/* ── Reminders / Reports wide panels ──────────────────────────────────────── */
/* color is set explicitly here (not left to inherit) because .dashboard-body
   forces a near-white text colour for the staff board's dark background —
   without this every heading/table cell in these light-surfaced panels would
   inherit that near-white colour and read as nearly invisible. */
.wide-panel {
  width: min(920px, 96vw);
  max-height: 90vh;
  padding: 0;
  gap: 0;
  overflow: hidden;
  color: var(--text-primary);
}

.wide-panel-header {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.wide-panel-header h2 { margin: 0; font-size: 18px; text-align: left; }
.wide-panel-header .modal-cancel { flex: none; align-self: auto; }

.reminders-body, .reports-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px 20px 24px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* Every direct child (headings, tables, bar charts, cards) defaults to
   flex-shrink:1 in this flex column — once total content exceeds the
   panel's max-height, children silently shrink instead of the column
   scrolling, and .reminders-table-wrap's own overflow-x:auto implicitly
   makes its overflow-y auto too (CSS2.1 overflow-x/y coupling), so a
   shrunk table just clips its body rows away with no scrollbar in sight.
   Same failure shape as .ji-section-header's flex-shrink fix elsewhere in
   this file — fixed the same way, at the container level so nothing new
   added here needs to remember it individually. */
.reminders-body > *, .reports-body > * { flex-shrink: 0; }
.reminders-body h3, .reports-body h3 { margin: 16px 0 4px; font-size: 14px; }
.reminders-body h3:first-child, .reports-body h3:first-child { margin-top: 0; }

.reminders-table-wrap { overflow-x: auto; }
.reminders-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.reminders-table th, .reminders-table td {
  text-align: left;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.reminders-table th {
  color: var(--text-muted);
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* ── All Drafts browser — EVERY open DRAFT invoice, hidden ones first then
   on-board ones, PIN-free to browse (see draftsBtn/draftsModal in
   index.html). Big tappable rows rather than a dense table — this is meant
   to be used by a mechanic on a tablet mid-shift, not an admin poring over
   a report. Rows are plain containers (not buttons) — the only interactive
   element is the per-hidden-row "Show on Board" pill; on-board rows are
   tag-only, mechanics still clock on at the tile itself. ────────────────── */
.older-jobs-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px 20px 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.older-job-row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  min-height: 56px;
  padding: 12px 16px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--surface-1);
  color: var(--text-primary);
  font-family: inherit;
  text-align: left;
}
.older-job-row-rego {
  flex: none;
  font-weight: 800;
  font-size: 16px;
  min-width: 84px;
}
.older-job-row-details {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.older-job-row-vehicle { font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.older-job-row-customer { font-size: 12px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.older-job-row-days {
  flex: none;
  font-size: 12px;
  font-weight: 700;
  color: var(--text-secondary);
  background: var(--muted-bg);
  border-radius: 999px;
  padding: 4px 10px;
  white-space: nowrap;
}

/* Same pill shape/size as .wake-draft-btn (admin Hidden Drafts panel) —
   this is the mechanic-facing equivalent, doing the same thing (adds the
   invoice to force-show) via POST /api/drafts/show, no PIN. */
.show-on-board-btn {
  flex: none;
  padding: 8px 14px;
  border: 1px solid var(--good);
  border-radius: 16px;
  background: var(--good);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.show-on-board-btn:hover { opacity: 0.9; }
.show-on-board-btn:disabled { opacity: 0.6; cursor: default; }

/* ── Labour Log (read-only, no PIN) — per-job session history, opened from a
   tile's main body when nobody's clocked on. Big, touch-friendly rows, same
   spirit as the Older Jobs drawer above. ─────────────────────────────────── */
.labour-log-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px 20px 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.labour-log-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  width: 100%;
  padding: 12px 16px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--surface-1);
}
.labour-log-row-live {
  border-color: var(--good);
  background: var(--good-bg);
}
.labour-log-mech {
  flex: none;
  font-weight: 800;
  font-size: 15px;
  min-width: 90px;
}
.labour-log-time {
  flex: none;
  font-size: 13px;
  color: var(--text-secondary);
}
.labour-log-duration {
  flex: 1;
  min-width: 140px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  text-align: right;
}
.labour-log-live-elapsed {
  flex: none;
  font-size: 13px;
  font-weight: 700;
  color: var(--good);
  font-variant-numeric: tabular-nums;
}
.labour-log-failed {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--critical-bg);
  color: var(--critical);
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.labour-log-total {
  display: flex;
  justify-content: space-between;
  margin-top: 4px;
  padding: 12px 16px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--muted-bg);
  font-weight: 800;
  font-size: 15px;
}

/* PART C — Remove from Board footer, bottom of the Labour Log popup, below
   the scrolling row list. DRAFT-only, no PIN — see renderLabourLogFooter in
   app.js. Full-width critical-toned pill so it reads as a deliberate,
   distinct action from the routine rows above it. */
.labour-log-footer {
  flex: none;
  padding: 12px 20px 20px;
  border-top: 1px solid var(--border);
}
.remove-from-board-btn {
  width: 100%;
  padding: 12px 16px;
  border-radius: 12px;
  border: 1px solid var(--critical);
  background: var(--critical-bg);
  color: var(--critical);
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
.remove-from-board-btn:hover:not(:disabled) { opacity: 0.85; }
.remove-from-board-btn:disabled {
  border-color: var(--border);
  background: var(--muted-bg);
  color: var(--text-muted);
  cursor: default;
  font-weight: 600;
}

/* WOF reminder sweep audit table (Reports, all-staff view only) — see
   wofReminderLog in server.js/app.js. */
.reminder-log-badge {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
}
.reminder-log-sent    { background: #2D6A4F; color: #fff; }
.reminder-log-skipped { background: var(--surface-2, #e0e0e0); color: var(--text-muted, #555); }

.reminder-actions { display: flex; gap: 6px; align-items: center; white-space: nowrap; }
.reminder-actions button {
  padding: 5px 10px;
  font-size: 12px;
  font-weight: 600;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--page-plane);
  color: var(--text-primary);
  cursor: pointer;
}
.reminder-actions button:disabled { opacity: 0.4; cursor: default; }
.reminder-actions button:not(:disabled):hover { border-color: var(--text-muted); }
.reminder-status { font-size: 12px; color: var(--text-muted); min-width: 60px; display: inline-block; }

.report-cards { display: flex; gap: 12px; flex-wrap: wrap; }
.report-card {
  flex: 1;
  min-width: 120px;
  background: var(--muted-bg);
  border-radius: 10px;
  padding: 14px;
  text-align: center;
}
.report-card-value { font-size: 26px; font-weight: 700; font-variant-numeric: tabular-nums; }
.report-card-label { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

.report-list { margin: 0; padding-left: 20px; font-size: 13px; }
.report-list li { margin-bottom: 4px; }

/* System Health (owner-only, 21 Jul 2026) — set visually apart from the
   rest of Reports with a top divider, since it's the one section Aljane/
   Sheila never see even though they share the same 'all' report scope. */
.system-health-section {
  margin-top: 24px;
  padding-top: 16px;
  border-top: 2px dashed var(--border);
}
.system-health-warn { color: var(--critical); }

/* Reports date-range selector row (Today / This Week / This Month / Custom) */
.report-range-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.report-range-btn {
  padding: 6px 14px;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--page-plane);
  color: var(--text-primary);
  font-size: 13px;
  cursor: pointer;
}
.report-range-btn:hover { border-color: var(--text-muted); }
.report-range-btn.active {
  background: var(--series-blue);
  border-color: var(--series-blue);
  color: #fff;
  font-weight: 600;
}

/* Admin-only "Summary" / "By Mechanic" toggle at the top of the all-staff
   Reports view (see reportsViewToggleHtml/renderMechanicReportSection in
   app.js) — same pill look as the date-range row above, own row so it
   never gets mixed up with .report-range-row's own click handling. */
.report-view-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}

/* One block per mechanic in the "By Mechanic" admin view — reuses
   .report-cards/.report-card and .reminders-table-wrap/.reminders-table
   verbatim (same visual structure as the single-mechanic Reports view),
   just repeated with a coloured name heading and a divider between
   mechanics. */
.mechanic-report-section {
  padding-top: 18px;
  margin-top: 18px;
  border-top: 1px solid var(--border);
}
.mechanic-report-section:first-child { padding-top: 0; margin-top: 0; border-top: none; }
.mechanic-report-heading { font-size: 20px; font-weight: 800; margin: 0 0 10px; }

/* Same pill look as .report-range-btn.active, but its own class — the
   range-switcher's click handler in app.js checks for .report-range-btn
   specifically (and returns early), so this button needs a different
   selector or its clicks would be swallowed by that handler instead of
   acknowledgeAllLabourFailures(). */
.labour-fail-ack-btn {
  padding: 6px 14px;
  border: 1px solid var(--critical);
  border-radius: 16px;
  background: var(--critical);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.labour-fail-ack-btn:hover { opacity: 0.9; }

/* Per-row retry button (see retryLabourFailure in app.js) — sits next to
   the "Unresolved" badge in the Failed Labour Writes table, so needs a
   little breathing room and its own disabled state for the round trip. */
.labour-fail-retry-btn { margin-left: 8px; padding: 4px 10px; font-size: 12px; }
.labour-fail-retry-btn:disabled { opacity: 0.6; cursor: default; }

/* One per row in the Hidden Drafts table (see hiddenDraftsBody in app.js) —
   same pill shape as the two buttons above, in the "good" green since waking
   a draft is a routine, harmless action (display-only, never touches Xero). */
.wake-draft-btn {
  padding: 4px 12px;
  border: 1px solid var(--good);
  border-radius: 16px;
  background: var(--good);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.wake-draft-btn:hover { opacity: 0.9; }

/* Admin force-show controls in the Hidden Drafts table — same pill shape as
   .wake-draft-btn above; colours are overridden by the retro CRT theme
   further down (these two buttons only ever render inside #hiddenDraftsModal). */
.force-show-btn,
.unpin-force-show-btn {
  padding: 4px 12px;
  border-radius: 16px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.force-show-btn:hover,
.unpin-force-show-btn:hover { opacity: 0.9; }

.report-range-custom { display: inline-flex; align-items: center; gap: 6px; }
.report-range-custom input[type="date"] {
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--page-plane);
  color: var(--text-primary);
  font-size: 13px;
}
.report-meta { font-size: 12px; margin: 2px 0 10px; }

/* Portrait touchscreen (see Task 1's keyboard) — Reports specifically needs
   bigger touch targets and larger text ("readable at arm's length"), which
   the existing max-width:640px "modal goes fullscreen" rule further down
   doesn't cover on its own (a 1080px-wide portrait screen is well past that
   breakpoint, but is still a screen mechanics read from a few steps back). */
@media (orientation: portrait) {
  .reports-body .report-range-btn {
    padding: 14px 22px;
    font-size: 18px;
    border-radius: 22px;
  }
  .reports-body .report-range-custom input[type="date"] {
    padding: 12px 14px;
    font-size: 16px;
  }
  .reports-body .report-card { padding: 20px 14px; min-width: 140px; }
  .reports-body .report-card-value { font-size: 34px; }
  .reports-body .report-card-label { font-size: 14px; }
  .reports-body .reminders-table { font-size: 17px; }
  .reports-body .reminders-table th, .reports-body .reminders-table td { padding: 12px 14px; }
  .reports-body .reminders-table th { font-size: 13px; }
  .reports-body .report-meta { font-size: 15px; }
  .reports-body h3 { font-size: 20px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   RETRO CRT TERMINAL THEME — Reporting modal (#reportsModal) and Unseen/
   Hidden Drafts view (#hiddenDraftsModal) ONLY. A reskin, not a rebuild:
   scoped entirely by these two modal IDs rather than the shared
   .reports-body/.reminders-table/.report-range-btn/etc. classes those
   panels reuse (the Reminders and Check-ins panels use the same classes and
   must stay untouched) — every rule below only touches colour/font/border,
   never layout, sizing, or the existing portrait touch-target scaling above.
   VT323 (a pixel/LCD-style display font) is used for headings only, where
   its size and glow read clearly; body/table/button text stays in IBM Plex
   Mono, a genuinely legible monospace face at small sizes — see the Google
   Fonts <link> in index.html's <head>. ══════════════════════════════════ */
:root {
  --crt-bg:        #000000;
  --crt-green:     #33FF33;
  --crt-green-dim: #1f9e1f;
  --crt-glow:      rgba(51, 255, 51, 0.65);
  --crt-font:      'IBM Plex Mono', 'Courier New', monospace;
  --crt-font-display: 'VT323', 'IBM Plex Mono', 'Courier New', monospace;
}

#reportsModal .modal-panel,
#hiddenDraftsModal .modal-panel {
  background: var(--crt-bg);
  border: 1px solid var(--crt-green-dim);
  font-family: var(--crt-font);
  color: var(--crt-green);
  position: relative;
  overflow: hidden; /* clips the scanline overlay below to the panel's rounded corners */
}

/* Hidden Drafts' table (Rego/Vehicle/Customer/Last Amended/Days Hidden +
   two action columns, nowrap cells) is wide enough that the base
   .wide-panel's 920px cap forced .reminders-table-wrap into a horizontal
   scrollbar on workshop screens. Override to most of the viewport width
   instead — the table then just lays out its columns naturally with no
   inner scroll — and a taller max-height so if anything's still tight it's
   the modal's own vertical scroll (.reports-body already has one) rather
   than the table clipping sideways. Reports gets the same treatment (its
   stat cards/tables were similarly cramped at 920px) — Reminders keeps the
   narrower default. */
#reportsModal .wide-panel,
#hiddenDraftsModal .wide-panel {
  width: min(1400px, 95vw);
  max-height: 94vh;
}

/* Horizontal scanline overlay — a static repeating-linear-gradient, not an
   image or a keyframe animation, so it costs nothing on a tablet GPU (no
   compositing/repaint work beyond the one-time paint of a plain background).
   Sits above the content but pointer-events:none so it never blocks a tap. */
#reportsModal .modal-panel::before,
#hiddenDraftsModal .modal-panel::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(51, 255, 51, 0.05) 0px,
    rgba(51, 255, 51, 0.05) 1px,
    transparent 1px,
    transparent 3px
  );
}

#reportsModal .wide-panel-header,
#hiddenDraftsModal .wide-panel-header {
  border-bottom: 1px solid var(--crt-green-dim);
}

#reportsModal .wide-panel-header h2,
#hiddenDraftsModal .wide-panel-header h2,
#reportsModal .reports-body h3,
#hiddenDraftsModal .reports-body h3 {
  font-family: var(--crt-font-display);
  color: var(--crt-green);
  letter-spacing: 0.04em;
  text-shadow: 0 0 6px var(--crt-glow), 0 0 14px var(--crt-glow);
}
#reportsModal .wide-panel-header h2,
#hiddenDraftsModal .wide-panel-header h2 { font-size: 30px; }
#reportsModal .reports-body h3,
#hiddenDraftsModal .reports-body h3 { font-size: 24px; }

#reportsModal .reports-body,
#hiddenDraftsModal .reports-body,
#reportsModal .report-list,
#hiddenDraftsModal .report-list,
#reportsModal .report-meta,
#hiddenDraftsModal .report-meta {
  color: var(--crt-green);
}

#reportsModal .modal-error,
#hiddenDraftsModal .modal-error {
  color: #ff5c5c; /* kept distinct from the theme green so an error never blends in */
}

#reportsModal .muted,
#hiddenDraftsModal .muted {
  color: var(--crt-green-dim);
}

/* Table */
#reportsModal .reminders-table th,
#hiddenDraftsModal .reminders-table th,
#reportsModal .reminders-table td,
#hiddenDraftsModal .reminders-table td {
  border-bottom: 1px solid var(--crt-green-dim);
  color: var(--crt-green);
}
#reportsModal .reminders-table th,
#hiddenDraftsModal .reminders-table th {
  color: var(--crt-green-dim);
}

/* Report stat cards + reminder-sweep audit badges */
#reportsModal .report-card,
#hiddenDraftsModal .report-card {
  background: #001a00;
  border: 1px solid var(--crt-green-dim);
  color: var(--crt-green);
}
#reportsModal .report-card-label,
#hiddenDraftsModal .report-card-label {
  color: var(--crt-green-dim);
}
#reportsModal .reminder-log-sent,
#hiddenDraftsModal .reminder-log-sent {
  background: var(--crt-green);
  color: #000;
}
#reportsModal .reminder-log-skipped,
#hiddenDraftsModal .reminder-log-skipped {
  background: #001a00;
  color: var(--crt-green-dim);
  border: 1px solid var(--crt-green-dim);
}
#reportsModal .reminder-status,
#hiddenDraftsModal .reminder-status {
  color: var(--crt-green-dim);
}

/* Buttons — green outline on black, inverting (solid green fill, black
   text) on press. Applies to every button inside these two views. */
#reportsModal .modal-cancel,
#hiddenDraftsModal .modal-cancel,
#reportsModal .report-range-btn,
#hiddenDraftsModal .report-range-btn,
#reportsModal .wake-draft-btn,
#hiddenDraftsModal .wake-draft-btn,
#reportsModal .force-show-btn,
#hiddenDraftsModal .force-show-btn,
#reportsModal .unpin-force-show-btn,
#hiddenDraftsModal .unpin-force-show-btn,
#reportsModal .labour-fail-ack-btn,
#hiddenDraftsModal .labour-fail-ack-btn,
#reportsModal .reminder-actions button,
#hiddenDraftsModal .reminder-actions button {
  font-family: var(--crt-font);
  background: var(--crt-bg);
  color: var(--crt-green);
  border: 1px solid var(--crt-green);
  text-decoration: none;
  text-shadow: none;
}
#reportsModal .report-range-btn.active,
#hiddenDraftsModal .report-range-btn.active {
  background: var(--crt-green);
  color: #000;
  border-color: var(--crt-green);
  font-weight: 700;
}
#reportsModal .modal-cancel:active,
#hiddenDraftsModal .modal-cancel:active,
#reportsModal .report-range-btn:active,
#hiddenDraftsModal .report-range-btn:active,
#reportsModal .wake-draft-btn:active,
#hiddenDraftsModal .wake-draft-btn:active,
#reportsModal .force-show-btn:active,
#hiddenDraftsModal .force-show-btn:active,
#reportsModal .unpin-force-show-btn:active,
#hiddenDraftsModal .unpin-force-show-btn:active,
#reportsModal .labour-fail-ack-btn:active,
#hiddenDraftsModal .labour-fail-ack-btn:active,
#reportsModal .reminder-actions button:active,
#hiddenDraftsModal .reminder-actions button:active {
  background: var(--crt-green);
  color: #000;
}

#reportsModal .report-range-custom input[type="date"],
#hiddenDraftsModal .report-range-custom input[type="date"] {
  background: var(--crt-bg);
  border: 1px solid var(--crt-green-dim);
  color: var(--crt-green);
  font-family: var(--crt-font);
}

/* Keeps the glow headings proportionally bigger on the portrait touch
   layout above, rather than the higher-specificity ID selector accidentally
   shrinking them back down to the non-portrait size on a tablet. */
@media (orientation: portrait) {
  #reportsModal .wide-panel-header h2,
  #hiddenDraftsModal .wide-panel-header h2 { font-size: 36px; }
  #reportsModal .reports-body h3,
  #hiddenDraftsModal .reports-body h3 { font-size: 28px; }
}

.bar-chart {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 120px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
.bar-chart-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
  min-width: 4px;
}
.bar-chart-bar { width: 100%; background: var(--series-blue); border-radius: 3px 3px 0 0; min-height: 2px; }
.bar-chart-label { font-size: 9px; color: var(--text-muted); margin-top: 4px; white-space: nowrap; }

/* ── "What Has Gone Into This Job" — dark-theme redesign ─────────────────── */
.job-items-panel {
  width: min(760px, 96vw);
  max-height: 92vh;
  padding: 0;
  gap: 0;
  background: #0f1117;
  border: 1px solid #2e3350;
  color: #f3f4f6;
  border-radius: 16px;
  overflow: hidden;
}

.ji-header {
  flex: none;
  padding: 18px 20px 14px;
  border-bottom: 1px solid #2e3350;
}

.ji-header h2 {
  margin: 0 0 8px;
  font-size: 18px;
  color: #f3f4f6;
}

.ji-job-info {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  font-size: 13px;
  color: #9ca3af;
}

.ji-job-info strong { color: #f3f4f6; }

.ji-job-info a.ji-phone-link {
  color: #f97316;
  text-decoration: none;
  font-weight: 600;
}
.ji-job-info a.ji-phone-link:hover { text-decoration: underline; }

#jobItemsModal .modal-error {
  margin: 10px 20px 0;
  text-align: left;
}

.ji-body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 14px 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ji-footer {
  flex: none;
  padding: 14px 20px;
  border-top: 1px solid #2e3350;
  display: flex;
  align-items: center;
  gap: 12px;
}

.ji-footer .modal-cancel { color: #9ca3af; flex: none; }

.ji-submit-btn {
  flex: 1;
  padding: 13px 16px;
  border-radius: 10px;
  border: none;
  background: #f97316;
  color: #0f1117;
  font-weight: 700;
  font-size: 15px;
  cursor: pointer;
}
.ji-submit-btn:hover { opacity: 0.92; }
.ji-submit-btn:disabled { opacity: 0.5; cursor: default; }

/* Navigation between the TLC Service and generic forms — Cancel discards
   everything from both; this button just switches which one is showing,
   without losing anything entered in either. */
.ji-nav-btn {
  flex: none;
  padding: 13px 14px;
  border-radius: 10px;
  border: 1px solid #f97316;
  background: none;
  color: #f97316;
  font-weight: 700;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}
.ji-nav-btn:hover { background: rgba(249, 115, 22, 0.12); }
.ji-nav-btn[hidden] { display: none; }

/* Collapsible sections */
.ji-section {
  background: #1a1d27;
  border: 1px solid #2e3350;
  border-radius: 12px;
  overflow: hidden;
  /* .ji-body (the parent) is a flex column — without this, flex items
     default to flex-shrink:1 and get silently squeezed to fit whatever
     space is left once several sections are expanded. Because this element
     also has overflow:hidden, the squeezed-out content (e.g. Steering's
     lower rows) was being clipped away entirely with no scrollbar ever
     appearing — that silent clip is what made these sections look
     "not scrollable" rather than genuinely capped-and-scrollable. */
  flex-shrink: 0;
}

.ji-section-header {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  background: none;
  border: none;
  color: #f3f4f6;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
}

.ji-section-icon { font-size: 18px; flex: none; }
.ji-section-title { flex: 1; }

.ji-section-chevron {
  flex: none;
  color: #9ca3af;
  transition: transform 0.2s ease;
}
.ji-section.expanded .ji-section-chevron { transform: rotate(90deg); }

.ji-section-body {
  padding: 4px 16px 16px;
  border-top: 1px solid #2e3350;
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Independently scrollable, capped height — a big checkbox grid (e.g.
     Steering's 17 items) no longer just grows the whole form taller; it
     scrolls within its own section instead. overscroll-behavior stops this
     inner scroll from "chaining" into the outer .ji-body scroll once it
     hits the top/bottom, which is what made touch-scrolling a long section
     feel broken on Android (a drag near the edge would hand off to the
     outer scroll instead of continuing to scroll the list). */
  max-height: 340px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
.ji-section-body[hidden] { display: none; }

/* Checkbox grids (Wipers/Headlights/Brakes/Steering/Recommendations/Battery) */
.ji-checkbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 8px;
  margin-top: 4px;
}

.ji-checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid #2e3350;
  background: #0f1117;
  font-size: 13px;
  cursor: pointer;
  user-select: none;
}

.ji-checkbox input { accent-color: #22c55e; width: 16px; height: 16px; flex: none; }

.ji-checkbox.checked {
  border-color: #22c55e;
  background: rgba(34, 197, 94, 0.12);
}

/* Standard/Euro Bosch wiper blade quantity (x1/x2) — sits below its
   checkbox as one grid cell in .ji-checkbox-grid, kept as a sibling (not
   nested inside the <label>) so tapping x1/x2 never also toggles the
   checkbox via the label's click-through behaviour. */
.ji-checkbox-with-qty { display: flex; flex-direction: column; gap: 6px; }
.ji-qty-select { width: 90px; flex: none; padding: 7px 8px; }

/* Toggle button groups (Undertray yes/no, removed/refitted/both/na) */
.ji-toggle-group {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.ji-toggle-group button {
  padding: 9px 16px;
  border-radius: 8px;
  border: 1px solid #2e3350;
  background: #0f1117;
  color: #f3f4f6;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.ji-toggle-group button.active {
  border-color: #22c55e;
  background: rgba(34, 197, 94, 0.16);
  color: #22c55e;
}

/* TLC Service form — large tap targets for Android tablets */
.tlc-health-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.tlc-health-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.tlc-health-label {
  font-size: 13px;
  font-weight: 600;
  color: #f3f4f6;
  flex: 1;
  min-width: 150px;
}

.tlc-rating-group button {
  min-width: 76px;
  min-height: 44px;
  text-align: center;
}

.tlc-rating-group button.rating-soon.active {
  border-color: #e65100;
  background: #e65100;
  color: #fff;
}
.tlc-rating-group button.rating-urgent.active {
  border-color: #c62828;
  background: #c62828;
  color: #fff;
}

/* Corner selector + per-corner spec builder shown under "Recommend Tyres"
   once rated Soon/Urgent — wraps onto its own line within the flex-wrap
   tlc-health-row above it. */
.tlc-tyre-spec-builder {
  flex-basis: 100%;
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 4px;
  padding: 10px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
}

.tlc-tyres-which-label {
  font-size: 12px;
  font-weight: 600;
  color: #9ca3af;
}

.tlc-corner-selector {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.tlc-corner-btn {
  min-width: 64px;
  min-height: 48px;
  padding: 10px 16px;
  border-radius: 8px;
  border: 1px solid #2e3350;
  background: #0f1117;
  color: #f3f4f6;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
}

.tlc-corner-btn.active {
  border-color: #f97316;
  background: rgba(249, 115, 22, 0.2);
  color: #f97316;
}

.tlc-corner-spec {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  border-radius: 8px;
  border: 1px solid #2e3350;
  background: #1a1d27;
}

.tlc-corner-spec-title {
  font-size: 13px;
  font-weight: 700;
  color: #f97316;
}

.tlc-corner-spec-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 8px;
}

.tlc-corner-spec-grid .ji-select {
  min-height: 44px;
}

/* KM reading banner — the TLC form's top-of-form field, and the inline
   variant shown under Wheel Alignment (TLC health check and the generic
   form's Steering section) once it's recommended/checked. */
.tlc-km-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 16px 18px;
  border-radius: 12px;
  background: rgba(249, 115, 22, 0.12);
  border: 1px solid rgba(249, 115, 22, 0.4);
}

.tlc-km-banner label {
  font-size: 15px;
  font-weight: 700;
  color: #f3f4f6;
}

.tlc-km-banner.tlc-km-inline {
  flex-basis: 100%;
  margin-top: 10px;
  padding: 12px 14px;
}

.tlc-km-input-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
}

.tlc-km-input {
  width: 140px;
  min-height: 52px;
  font-size: 22px;
  font-weight: 700;
  text-align: right;
  letter-spacing: 0.04em;
}

.tlc-km-suffix {
  flex: none;
  font-size: 18px;
  font-weight: 700;
  color: #f97316;
}

.tlc-tyre-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}

.tlc-tyre-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.tlc-tyre-field label {
  font-size: 12px;
  font-weight: 600;
  color: #9ca3af;
}

.tlc-tyre-input {
  min-height: 48px;
  font-size: 18px;
  font-weight: 700;
  text-align: center;
}

.tlc-tyre-input.tyre-green  { border-color: #22c55e; background: rgba(34, 197, 94, 0.14); color: #22c55e; }
.tlc-tyre-input.tyre-orange { border-color: #f59e0b; background: rgba(245, 158, 11, 0.14); color: #f59e0b; }
.tlc-tyre-input.tyre-red    { border-color: #ef4444; background: rgba(239, 68, 68, 0.14); color: #ef4444; }

/* Shared field styling */
.ji-item-input, .ji-qty-input, .ji-select, .ji-qty-select, textarea.ji-notes, .tlc-tyre-input, .tlc-km-input {
  background: #0f1117;
  border: 1px solid #2e3350;
  border-radius: 8px;
  color: #f3f4f6;
  padding: 9px 10px;
  font-size: 13px;
}

.ji-item-input:focus, .ji-qty-input:focus, .ji-select:focus, .ji-qty-select:focus, textarea.ji-notes:focus, .tlc-tyre-input:focus, .tlc-km-input:focus {
  outline: 2px solid #f97316;
  outline-offset: 1px;
}

.ji-select { width: 100%; }

/* Repeatable rows (Oils Used / Filters Used) */
.ji-repeat-rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.ji-repeat-row {
  display: flex;
  gap: 8px;
  align-items: center;
}

.ji-item-input { width: 100%; }
.ji-qty-input { width: 90px; flex: none; }

/* Custom searchable list (Oils, Filters, Battery) — replaces the native
   <datalist>, which Android Chrome renders too short/inconsistently for a
   ~1800-item catalog. */
.ji-autocomplete { position: relative; min-width: 0; }
.ji-repeat-row .ji-autocomplete { flex: 1; }

.ji-autocomplete-results {
  position: fixed;
  z-index: 40;
  background: #171a24;
  border: 1px solid #2e3350;
  border-radius: 10px;
  max-height: 344px; /* ~7 rows at the 48px+border row height below */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scroll-behavior: smooth;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.5);
}

.ji-autocomplete-results[hidden] { display: none; }

.ji-autocomplete-result {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 48px;
  padding: 10px 14px;
  background: none;
  border: none;
  border-bottom: 1px solid #23273a;
  color: #f3f4f6;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}

.ji-autocomplete-result:last-child { border-bottom: none; }

.ji-autocomplete-result:active,
.ji-autocomplete-result:hover {
  background: rgba(249, 115, 22, 0.16);
}

.ji-ac-code {
  flex: none;
  font-weight: 700;
  font-size: 12px;
  padding: 3px 7px;
  border-radius: 6px;
  background: rgba(249, 115, 22, 0.18);
  color: #f97316;
  white-space: nowrap;
}

.ji-ac-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Oil filter codes not yet added to Xero — still selectable (invoices as a
   $0 descriptive line, see buildJiItemsPayload/buildTlcItemsPayload), just
   flagged so the mechanic isn't surprised the job costs nothing for it. */
.ji-ac-not-in-xero {
  flex: none;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 7px;
  border-radius: 6px;
  background: rgba(148, 163, 184, 0.18);
  color: #94a3b8;
  white-space: nowrap;
}

.ji-remove-row {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 8px;
  border: 1px solid #2e3350;
  background: #0f1117;
  color: #ef4444;
  font-size: 15px;
  cursor: pointer;
}

.ji-add-row {
  align-self: flex-start;
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px dashed #f97316;
  background: none;
  color: #f97316;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

/* Liquids rows */
.ji-liquid-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.ji-liquid-label { font-size: 13px; color: #f3f4f6; }
.ji-liquid-row .ji-qty-input { width: 110px; }

/* Undertray */
.ji-undertray-detail {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 4px;
}

.ji-clips-input { width: 140px; }

/* Notes */
textarea.ji-notes {
  width: 100%;
  min-height: 100px;
  resize: vertical;
  font-family: inherit;
}

/* Board note (see boardNoteSectionHtml) — distinct from the section above:
   this one posts to /api/job-note, not the invoice. */
.ji-board-note-hint {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--text-secondary);
}

/* ── Small screens: the big modal panels go full-screen ───────────────────── */
@media (max-width: 640px) {
  .job-items-panel { width: 100vw; max-height: 100vh; height: 100vh; border-radius: 0; }
  #jobItemsModal.modal-overlay { padding: 0; }
  .wide-panel { width: 100vw; max-height: 100vh; height: 100vh; border-radius: 0; }
  /* #reportsModal/#hiddenDraftsModal .wide-panel's 95vw/94vh override above
     beats this rule by specificity (ID selector) at every width, so they
     need their own copy here or Reports/Hidden Drafts would stop going
     full-bleed on phones. */
  #reportsModal .wide-panel,
  #hiddenDraftsModal .wide-panel { width: 100vw; max-height: 100vh; height: 100vh; border-radius: 0; }
  #remindersModal.modal-overlay, #reportsModal.modal-overlay { padding: 0; }
}

/* ── Empty state & footer ─────────────────────────────────────────────────── */
.empty-state:not([hidden]) {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 40px;
  text-align: center;
  /* Fixed light grey, not var(--text-secondary) — this sits directly on the
     dashboard's fixed-black page background (see .dashboard-body), not a
     theme-following surface, so it needs to stay readable regardless of
     system light/dark preference. */
  color: #9ca3af;
}

.empty-state a.connect-btn {
  padding: 10px 18px;
  border-radius: 8px;
  background: var(--series-blue);
  color: #fff;
  text-decoration: none;
  font-weight: 600;
}

.foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 24px;
  border-top: 1px solid var(--border);
  background: var(--surface-1);
  color: var(--text-primary);
}

.muted { color: var(--text-muted); font-size: 12px; }

.job-type-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  font-size: 11px;
  color: var(--text-muted);
}

.job-type-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}

.job-type-legend-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  flex: none;
  white-space: nowrap;
}

.ghost-btn {
  padding: 8px 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-secondary);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.ghost-btn:hover { border-color: var(--text-muted); }
.ghost-btn:disabled { opacity: 0.5; cursor: default; }

/* TLC Service success celebration — full-screen overlay shown briefly after
   a TLC form's line items are confirmed written to Xero (see
   tlc-success-overlay handling in submitCombinedForm). Colour is set inline
   per-mechanic via --mech-color/--mech-text, same variables as mechBlockHtml. */
.tlc-success-overlay {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 11, 16, 0.92);
  animation: tlcSuccessFadeIn 0.25s ease-out;
}
.tlc-success-overlay[hidden] { display: none; }

.tlc-success-message {
  font-size: clamp(36px, 8vw, 72px);
  font-weight: 800;
  text-align: center;
  padding: 0.4em 0.8em;
  border-radius: 24px;
  background: var(--mech-color, #22c55e);
  color: var(--mech-text, #fff);
  border: 2px solid var(--mech-border, transparent);
  box-shadow: 0 0 60px 10px var(--mech-color, transparent);
  animation: tlcSuccessPop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes tlcSuccessFadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes tlcSuccessPop {
  from { transform: scale(0.7); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

/* ── RETRO CALCULATOR PIN PADS ───────────────────────────────────────────────
   Every PIN-entry surface (app-lock, mechanic clock-on/off, review/note/
   check-in dismiss, reports gate) shares the same plain .pin-dots/.keypad
   markup — each is now wrapped in a `.retro-calc` calculator-body div in
   index.html for an old-school Casio look. Purely cosmetic: app.js's PIN
   logic (submitAppPin, the pinKeypad click handler, etc.) only ever touches
   .pin-dot/.filled and [data-digit]/[data-action], all still present as-is. */

@font-face {
  font-family: 'DSEG7';
  src: url('https://cdn.jsdelivr.net/npm/dseg@0.46.0/fonts/DSEG7-Classic/DSEG7Classic-Bold.woff2') format('woff2');
  font-weight: bold;
  font-style: normal;
  font-display: swap;
}

.retro-calc {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 12px 16px;
  border-radius: 18px;
  background: linear-gradient(160deg, #46464a 0%, #2b2b2e 45%, #19191b 100%);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.08) inset,
    0 -8px 14px rgba(0, 0, 0, 0.5) inset,
    0 10px 20px rgba(0, 0, 0, 0.4);
  border: 1px solid #000;
}

.retro-calc-brand {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 4px;
}

.retro-calc-brand-name {
  font-family: Georgia, 'Times New Roman', serif;
  font-style: italic;
  font-weight: 700;
  letter-spacing: 0.03em;
  font-size: 15px;
  color: #dcdce0;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.6);
}

/* Fake solar-panel strip, the 80s-calculator flourish next to the brand name */
.retro-calc-solar {
  display: flex;
  gap: 3px;
  padding: 4px 6px;
  border-radius: 3px;
  background: linear-gradient(155deg, #24347a, #0c1636 70%);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.6);
}

.retro-calc-solar span {
  width: 5px;
  height: 13px;
  border-radius: 1px;
  background: linear-gradient(155deg, #5678e0, #1c2a63);
}

/* LCD screen — recessed glass look, seven-segment PIN dashes */
.retro-calc-display {
  background: linear-gradient(180deg, #4a5f4d 0%, #37452f 55%, #2c3928 100%);
  border-radius: 6px;
  padding: 12px 10px;
  border: 1px solid #1a2118;
  box-shadow:
    inset 0 3px 7px rgba(0, 0, 0, 0.55),
    inset 0 -1px 0 rgba(255, 255, 255, 0.05);
}

.retro-calc-display .pin-dots { gap: 8px; justify-content: center; }

/* Ghost "8" (all segments faintly lit, like a real LCD's dead pixels) sits
   behind a bright "-" that only shows once a digit's been entered. */
.retro-calc-display .pin-dots .pin-dot {
  width: auto;
  height: auto;
  min-width: 20px;
  border: none;
  border-radius: 0;
  background: none;
  font-family: 'DSEG7', monospace;
  font-size: 28px;
  line-height: 1;
  text-align: center;
  color: rgba(255, 255, 255, 0.08);
}

.retro-calc-display .pin-dots .pin-dot::before { content: "8"; }

.retro-calc-display .pin-dots .pin-dot.filled {
  color: #d9ffcf;
  text-shadow: 0 0 6px rgba(200, 255, 170, 0.85), 0 0 16px rgba(150, 255, 120, 0.5);
}

.retro-calc-display .pin-dots .pin-dot.filled::before { content: "-"; }

/* Keys — chunky 3D-raised buttons, colour-coded like a classic pocket
   calculator: green digits, orange Clear, red backspace/delete. */
.retro-calc .keypad { gap: 8px; }

.retro-calc .keypad button {
  padding: 16px 0;
  font-size: 20px;
  font-weight: 800;
  font-family: inherit;
  border-radius: 8px;
  border: none;
  color: #142214;
  background: linear-gradient(180deg, #3ee158, #22b23e 55%, #1c9a35);
  box-shadow:
    0 4px 0 #157a29,
    0 6px 9px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.35);
  cursor: pointer;
  transition: transform 0.06s ease, box-shadow 0.06s ease;
}

.retro-calc .keypad button:active {
  transform: translateY(3px);
  box-shadow:
    0 1px 0 #157a29,
    0 2px 4px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

.retro-calc .keypad button[data-action="clear"] {
  color: #331700;
  background: linear-gradient(180deg, #ffa64d, #ff851b 55%, #e06e0c);
  box-shadow:
    0 4px 0 #a8560e,
    0 6px 9px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.4);
}

.retro-calc .keypad button[data-action="clear"]:active {
  box-shadow:
    0 1px 0 #a8560e,
    0 2px 4px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

.retro-calc .keypad button[data-action="back"] {
  color: #fff;
  background: linear-gradient(180deg, #f2554f, #e53935 55%, #c22a26);
  box-shadow:
    0 4px 0 #8c1e1a,
    0 6px 9px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

.retro-calc .keypad button[data-action="back"]:active {
  box-shadow:
    0 1px 0 #8c1e1a,
    0 2px 4px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

/* ── ON-SCREEN VIRTUAL KEYBOARD (touch stations only) ─────────────────────
   Only ever shown by app.js when the active pointer is coarse (touch) — see
   isCoarsePointer(). Fixed, horizontally centred; app.js sets only `top`
   (above or below the focused input, whichever has room). z-index above
   every other overlay in this file (highest previously was 999) so it's
   never hidden behind a modal or the TLC success celebration. */
.virtual-keyboard {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  width: min(560px, 96vw);
  z-index: 1000;
  background: #1c1c1e;
  border: 1px solid #000;
  border-radius: 14px;
  padding: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.virtual-keyboard[hidden] { display: none; }

.vk-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 2px 6px 8px;
  color: #9a9a9e;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.vk-close {
  border: none;
  background: #333336;
  color: #fff;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

.vk-row {
  display: flex;
  gap: 6px;
  margin-bottom: 6px;
}
.vk-row:last-child { margin-bottom: 0; }

.vk-key {
  flex: 1;
  min-width: 0;
  height: 44px;
  border: none;
  border-radius: 8px;
  background: #3a3a3d;
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
}
.vk-key:active { background: #55555a; }

.vk-key-wide { flex: 1.6; font-size: 13px; }
.vk-key-space { flex: 6; }
.vk-key-enter { background: #2a78d6; }
.vk-key-enter:active { background: #3987e5; }
.vk-key.active { background: #55555a; box-shadow: inset 0 0 0 2px #9a9a9e; }

/* Portrait touchscreen (the 24" workshop station runs tall, e.g.
   1080x1920) — a full-width dock pinned to the bottom of the screen, like
   a real device keyboard, with much bigger keys for gloved/greasy fingers.
   app.js clears any leftover inline `top` from a landscape positioning and
   scrolls the focused INPUT up above this dock instead of moving the
   keyboard (see scrollInputAboveKeyboard) — landscape/desktop is
   unaffected, this query only matches a tall/narrow viewport. */
@media (orientation: portrait) {
  .virtual-keyboard {
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    transform: none;
    width: 100%;
    border-radius: 20px 20px 0 0;
    padding: 14px 12px calc(14px + env(safe-area-inset-bottom, 0px));
  }
  .vk-header { padding: 4px 10px 14px; }
  .vk-title { font-size: 15px; }
  .vk-close { width: 42px; height: 42px; font-size: 19px; }
  .vk-row { gap: 10px; margin-bottom: 10px; }
  .vk-key {
    height: 72px;
    font-size: 26px;
    border-radius: 12px;
  }
  .vk-key-wide { font-size: 18px; }
}
