/* ---------------------------------------------------------------------------
   The lighter look — an Apple-ish reading of the app: white cards floating on
   a soft grey ground, no hard borders, pill buttons in a bright blue rather
   than a heavy dark fill, and movement that eases instead of snapping.

   Loaded only by a page whose <body class="theme-minimal"> opts in, right
   after style.css. Nothing here reaches a page that doesn't load this file,
   so the rest of the app is untouched while this is being judged.

   Every rule only overrides a variable or a class that style.css already
   defines, so once it earns its keep it folds straight into that file.
   --------------------------------------------------------------------------- */

body.theme-minimal {
  /* Anything still reaching for the old brand variables lands on these. */
  --brand-navy: #0071e3;
  --brand-navy-dark: #0077ed;
  --brand-gold: #ff9500;
  --brand-cyan: #0071e3;

  --bg: #f5f5f7;
  --bg-alt: #ececf0;
  --panel: #ffffff;
  --border: rgba(0, 0, 0, 0.08);
  --text: #1d1d1f;
  --text-muted: #6e6e73;
  --primary: #0071e3;
  --primary-dark: #0077ed;      /* Apple brightens on hover rather than darkening */
  --primary-soft: #eaf3fd;
  --accent: #0071e3;
  --danger: #d70015;
  --danger-bg: #ffeceb;
  --success-bg: #e8f8ee;
  --radius: 18px;          /* cards. the only others: 12 fields, 20 sheets, pill */

  /* Apple uses two blues, and it is worth copying: #0071e3 fills a button,
     #0066cc sets a text link. The darker one holds its contrast against white
     at 13 and 14px, where the button blue starts to glare. */
  --link: #0066cc;

  /* There is not a single box-shadow on apple.com — every element, checked.
     Surfaces are told apart by tone and a hairline, which is what a dense
     table wants anyway: a shadow under every panel on a page of six panels
     reads as clutter long before it reads as depth. The variable stays so
     anything still asking for it gets nothing rather than breaks. */
  --shadow: none;
  --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.18);   /* modals only, where it is real */
  --sticky-top: 52px;           /* the bar, which no longer carries a hairline */

  /* The one that does the most work: text rendered the way macOS renders it. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  /* Body copy on the macOS scale, with the tracking Apple publishes for this
     size rather than one value applied to everything. */
  font-size: 15px;
  line-height: 1.47;
  letter-spacing: var(--track-15);
  background: var(--bg);
}

/* A button, a dropdown and a text field do NOT inherit the page's typeface —
   the browser puts its own on them unless told otherwise. style.css sets the
   family once, on :root, so every control on every screen was quietly running
   a different font. This is the whole of the "multiple fonts" problem. */
body.theme-minimal button,
body.theme-minimal input,
body.theme-minimal select,
body.theme-minimal textarea,
body.theme-minimal optgroup,
body.theme-minimal option { font-family: inherit; }

/* Apple's own link blue — #0066cc, darker than the #0071e3 that fills a
   button, because at 12 and 13px the button blue starts to glare on white. */
body.theme-minimal a { color: var(--link); text-decoration: none; }
body.theme-minimal a:hover { text-decoration: underline; }
/* Half the buttons in this app are links wearing .btn. The rule above is more
   specific than .btn's own colour, so without these two they come out blue on
   blue and the label vanishes — which is exactly what happened first time. */
body.theme-minimal a.btn { color: #fff; }
body.theme-minimal a.btn.secondary { color: var(--text); }

body.theme-minimal .page-wrap { padding-top: 40px; padding-bottom: 80px; }
/* The macOS type scale, because this is a working screen and not a poster:
   Title 1 for the page, Title 2 for a panel, Title 3 under that, Body at
   14/19 in the tables. Each size carries the tracking Apple publishes for it —
   note that 25 and 28 are POSITIVE. Tightening a heading because it is large
   is the instinct that makes type look almost right. */
body.theme-minimal h1 { font-size: 25px; line-height: 31px; font-weight: 600; letter-spacing: var(--track-25); }
body.theme-minimal h2 { font-size: 19px; line-height: 24px; font-weight: 600; letter-spacing: var(--track-19); }
body.theme-minimal h3 { font-size: 17px; line-height: 22px; font-weight: 600; letter-spacing: var(--track-17); }
/* 150 characters on one line is not a sentence, it is a ticker. */
body.theme-minimal .subtitle { color: var(--text-muted); font-size: 15px; line-height: 1.47; letter-spacing: var(--track-15); margin-bottom: 28px; max-width: 68ch; }

/* Body, Callout, Subhead, Caption — the rest of the ladder, applied where the
   app already had ad-hoc sizes. A table is Body; the row above it is Subhead
   in the muted ink, never bold, because a column name is a label and not a
   heading; a hint is Caption. */
body.theme-minimal table { font-size: 14px; line-height: 19px; letter-spacing: var(--track-14); }
body.theme-minimal .hint { font-size: 12px; line-height: 16px; letter-spacing: var(--track-12); }
/* A paragraph of explanation was running the full 1082px of its panel — about
   a hundred and eighty characters a line, which no one reads twice. Apple
   never lets a measure past roughly seventy-five characters, so neither does
   this. An inline `.hint` beside a heading is unaffected: max-width does
   nothing to an inline box. */
body.theme-minimal p.hint,
body.theme-minimal .panel > p { max-width: 78ch; }

/* A heading that wraps should break where the sentence does, not wherever the
   box happens to end, and a paragraph should not leave one word on the last
   line. Both are ignored by browsers that do not have them. */
body.theme-minimal h1,
body.theme-minimal h2,
body.theme-minimal h3 { text-wrap: balance; }
body.theme-minimal p, body.theme-minimal .subtitle { text-wrap: pretty; }
body.theme-minimal label { font-size: 12px; line-height: 16px; font-weight: 400; letter-spacing: var(--track-12); color: var(--text-muted); }

/* ---- Top bar: frosted, not painted ------------------------------------- */
/* Apple's bar is 48px and has no border under it at all — the blur alone
   separates it from what scrolls past. 52 here, because this one carries a
   logo lockup rather than a glyph. --sticky-top must follow any change to
   this number, or the frozen table headers park in the wrong place. */
body.theme-minimal .topnav {
  height: 52px;
  background: rgba(255, 255, 255, 0.72);
  -webkit-backdrop-filter: saturate(1.8) blur(20px);
  backdrop-filter: saturate(1.8) blur(20px);
  border-bottom: none;
  color: var(--text);
  box-shadow: none;
}
/* The nav used to darken a white cut-out with a filter. It carries the real
   lockup now — in the company's own colours — so nothing is filtered. */
body.theme-minimal .topnav .brand-text .sub { color: var(--text-muted); opacity: 1; }

/* Nav items as a macOS segmented control: a soft pill, no underline. */
body.theme-minimal .topnav-links a {
  padding: 7px 13px;
  margin: 0 1px;
  border-radius: 980px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text-muted);
  transition: background-color 0.22s cubic-bezier(0.4, 0, 0.2, 1), color 0.22s ease;
}
body.theme-minimal .topnav-links a:hover { background: rgba(0, 0, 0, 0.05); color: var(--text); text-decoration: none; }
body.theme-minimal .topnav-links a.active { background: rgba(0, 0, 0, 0.075); color: var(--text); font-weight: 600; }
body.theme-minimal .topnav-links a.active::after { display: none; }

/* The Bills button is one of the segments, so it is shaped like one. */
body.theme-minimal .nav-group-btn {
  padding: 7px 13px;
  margin: 0 1px;
  border-radius: 980px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text-muted);
  transition: background-color 0.22s cubic-bezier(0.4, 0, 0.2, 1), color 0.22s ease;
}
body.theme-minimal .nav-group-btn:hover { background: rgba(0, 0, 0, 0.05); color: var(--text); }
body.theme-minimal .nav-group-btn.active,
body.theme-minimal .nav-group-btn.is-open { background: rgba(0, 0, 0, 0.075); color: var(--text); font-weight: 600; }
body.theme-minimal .nav-menu {
  background: var(--panel);
  border-radius: 14px;
  padding: 7px;
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.14);
}

body.theme-minimal .topnav-right span { color: var(--text-muted); font-size: 13.5px; }
body.theme-minimal .topnav-right button {
  background: rgba(0, 0, 0, 0.05);
  color: var(--text);
  border: none;
  border-radius: 980px;
  padding: 7px 15px;
  font-size: 13px;
  font-weight: 500;
  transition: background-color 0.2s ease, transform 0.12s ease;
}
body.theme-minimal .topnav-right button:hover { background: rgba(0, 0, 0, 0.09); }
body.theme-minimal .topnav-right button:active { transform: scale(0.96); }

/* ---- Buttons: pills, bright fill, a press you can feel ------------------ */
/* Measured off apple.com: #0071e3, radius 980px, padding 11/21 — and weight
   400, not the 500 or 600 that feels safer. A filled pill in Apple blue is
   already emphatic; bolding the label on top of it is what made these read
   heavy. */
body.theme-minimal .btn {
  border-radius: 980px;
  padding: 10px 21px;
  font-size: 15px;
  font-weight: 400;
  letter-spacing: var(--track-15);
  box-shadow: none;
  transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1),
              color 0.2s ease,
              transform 0.12s cubic-bezier(0.4, 0, 0.2, 1);
}
body.theme-minimal .btn:hover { text-decoration: none; }
body.theme-minimal .btn:active { transform: scale(0.96); }
body.theme-minimal .btn.small { padding: 7px 15px; font-size: 13px; font-weight: 400; letter-spacing: var(--track-13); line-height: 16px; }

/* Body copy now carries Apple's 1.47 leading, and a control must not inherit
   it: 13px of label at 1.47 plus 14px of padding is a 33px pill, which broke
   the rule that every control in a table row is 30px. Controls set their own.
   `.btn.small` is given the exact 16px that lands it back on 30. */
body.theme-minimal .btn { line-height: 1.25; }
body.theme-minimal input,
body.theme-minimal select,
body.theme-minimal textarea { line-height: 1.3; }
body.theme-minimal .badge { line-height: 1.3; }
body.theme-minimal .topnav-links a,
body.theme-minimal .nav-group-btn { line-height: 1.2; }
body.theme-minimal .btn.secondary { background: rgba(0, 0, 0, 0.05); color: var(--text); }
body.theme-minimal .btn.secondary:hover { background: rgba(0, 0, 0, 0.09); }
body.theme-minimal .btn:disabled { opacity: 0.4; }
body.theme-minimal .btn:disabled:active { transform: none; }

/* ---- Cards: white on grey, shape from shadow rather than a line --------- */
/* A hairline where there used to be a shadow. White card on the grey ground
   is already a step; the line finishes the edge without pretending the card
   is floating above the page. */
body.theme-minimal .panel,
body.theme-minimal .stat-card,
body.theme-minimal .portal-card,
body.theme-minimal .bill-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: none;
  background: var(--panel);
}
body.theme-minimal .panel { padding: 22px 24px; margin-bottom: 20px; }
body.theme-minimal .stat-row { gap: 16px; margin-bottom: 24px; }
body.theme-minimal .stat-card { padding: 20px 22px; }
/* 28px sits between Large Title and Title 1, and Apple opens the tracking up
   at that size rather than closing it. */
body.theme-minimal .stat-card .value { font-size: 28px; line-height: 34px; font-weight: 600; letter-spacing: var(--track-28); }
body.theme-minimal .stat-card .label {
  color: var(--text-muted);
  font-size: 13px;
  font-weight: 400;
  text-transform: none;      /* Title Case, per the house rule — not shouted */
  letter-spacing: -0.01em;
  margin-top: 3px;
}

/* A card that is also a button lifts a little under the pointer. */
body.theme-minimal .stat-card.stat-action,
body.theme-minimal .portal-card {
  transition: transform 0.25s cubic-bezier(0.32, 0.72, 0, 1), box-shadow 0.25s ease;
}
body.theme-minimal .stat-card.stat-action:hover,
body.theme-minimal .portal-card:hover {
  background: var(--panel);
  transform: translateY(-2px);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05), 0 14px 34px rgba(0, 0, 0, 0.09);
}
body.theme-minimal .stat-card.stat-action:active,
body.theme-minimal .portal-card:active { transform: translateY(0); }
/* A card that is a link keeps the page's text colour, not link blue. */
body.theme-minimal a.stat-card { color: inherit; display: block; text-decoration: none; }
body.theme-minimal a.stat-card:hover { text-decoration: none; }

/* ---- Tables: hairlines, roomy rows, headings in Title Case ------------- */
/* Air is added DOWNWARDS only. Sideways padding stays a shade under what
   style.css had (12px), because several tables here are already as wide as
   their panel — the staff list overflowed its card the moment the cells got
   wider, and an overflowing table loses its frozen header. */
body.theme-minimal table th,
body.theme-minimal table td { padding: 11px; border-bottom: 1px solid rgba(0, 0, 0, 0.06); }
body.theme-minimal table thead th {
  background: var(--panel);
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: var(--track-12);
  border-bottom: 1px solid rgba(0, 0, 0, 0.09);
}
body.theme-minimal table tbody tr { transition: background-color 0.18s ease; }
body.theme-minimal table tbody tr:hover { background: rgba(0, 0, 0, 0.025); }

/* A row that opens a panel says so twice: the cursor changes, and the cell
   that names the thing reads as a link. Every table here already tints on
   hover, so the tint alone could not tell an openable row from any other. */
body.theme-minimal tbody tr.row-open { cursor: pointer; }
body.theme-minimal tbody tr.row-open:hover .row-open-label,
body.theme-minimal tbody tr.row-open:focus-visible .row-open-label {
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
/* Inside a scrolling wrapper an outline painted outside the row is clipped,
   the same trap the nav links hit, so this one sits inboard. */
body.theme-minimal tbody tr.row-open:focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: -2px;
}
body.theme-minimal tbody tr.row-open:focus-visible > td:first-child { border-top-left-radius: 6px; border-bottom-left-radius: 6px; }

/* The last row's rule would otherwise float above the card's bottom padding
   with nothing under it. */
body.theme-minimal table tbody tr:last-child td { border-bottom: none; }

/* ---- Status: a dot and a word, in the system colours ------------------- */
body.theme-minimal .badge {
  background: transparent !important;
  padding: 0;
  border-radius: 0;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: 500;
}
body.theme-minimal .badge::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
body.theme-minimal .badge.active,
body.theme-minimal .badge.finalized { color: #1a7f37; }
body.theme-minimal .badge.inactive { color: var(--text-muted); }
body.theme-minimal .badge.draft { color: #9a6700; }
body.theme-minimal .badge.released { color: var(--accent); }

/* ---- Fields: soft corners and a focus ring, not an outline ------------- */
body.theme-minimal input[type=text],
body.theme-minimal input[type=number],
body.theme-minimal input[type=email],
body.theme-minimal input[type=password],
body.theme-minimal input[type=date],
body.theme-minimal input[type=month],
body.theme-minimal input[type=tel],
body.theme-minimal select,
body.theme-minimal textarea {
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 12px;
  padding: 10px 13px;
  font-size: 14.5px;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
body.theme-minimal input:focus,
body.theme-minimal select:focus,
body.theme-minimal textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.18);
}
body.theme-minimal label { font-weight: 500; color: var(--text); font-size: 13.5px; }

/* ---- Panels that open over the page ------------------------------------ */
/* The backdrop is the frosted layer AND was the scroll container, so every
   frame of a scroll inside a tall panel made the browser re-sample and re-blur
   the whole viewport — which is what flickered. The panel scrolls inside
   itself now and the frosted layer never moves, so the blur is computed once. */
body.theme-minimal .modal-backdrop {
  background: rgba(0, 0, 0, 0.32);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  animation: tmFade 0.22s ease both;
  overflow: hidden;
  align-items: center;
}
body.theme-minimal .modal {
  max-height: calc(100vh - 64px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* Two blurred layers stacked is worse than one, and behind a 32% black scrim
   the bar's own blur buys nothing. */
body.theme-minimal.modal-open .topnav {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
body.theme-minimal .modal {
  border-radius: 20px;
  padding: 28px 30px;
  box-shadow: var(--shadow-lg);
  animation: tmSheet 0.36s cubic-bezier(0.32, 0.72, 0, 1) both;
}
body.theme-minimal .modal-close {
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.05);
  color: var(--text-muted);
  transition: background-color 0.2s ease;
}
body.theme-minimal .modal-close:hover { background: rgba(0, 0, 0, 0.09); color: var(--text); }

/* ---- Messages ---------------------------------------------------------- */
body.theme-minimal .toast {
  border-radius: 12px;
  background: rgba(29, 29, 31, 0.86);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.22);
}
body.theme-minimal .toast-success { background: rgba(26, 127, 55, 0.92); }
body.theme-minimal .toast-error { background: rgba(215, 0, 21, 0.92); }
body.theme-minimal .error-text,
body.theme-minimal .success-text { border-radius: 12px; border: none; }
body.theme-minimal .pill { border-radius: 980px; background: rgba(0, 0, 0, 0.05); }

/* ---- Movement ----------------------------------------------------------
   Cards ease up into place on load, one just after the other, the way a
   settings pane does. It has to be short enough not to be a wait: this runs
   AFTER the figures have arrived, so every tenth of a second here is added to
   every click. It was 0.45s with a 0.18s stagger — up to 0.63s of a page
   that was already loaded, still fading in — and that was a third of what
   made the site feel slow (21 Sept 2026). 0.15s and a 6px rise still reads
   as movement; do not lengthen it again. */
@keyframes tmRise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@keyframes tmSheet {
  from { opacity: 0; transform: scale(0.96) translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@keyframes tmFade { from { opacity: 0; } to { opacity: 1; } }

body.theme-minimal .panel,
body.theme-minimal .stat-card { animation: tmRise 0.15s cubic-bezier(0.32, 0.72, 0, 1) both; }
body.theme-minimal .stat-row .stat-card:nth-child(2) { animation-delay: 0.02s; }
body.theme-minimal .stat-row .stat-card:nth-child(3) { animation-delay: 0.04s; }
body.theme-minimal .stat-row .stat-card:nth-child(4) { animation-delay: 0.06s; }

/* Someone who has asked their machine to stop animating things gets none of
   it — the setting exists for people who are made ill by motion. */
@media (prefers-reduced-motion: reduce) {
  body.theme-minimal *,
  body.theme-minimal *::before,
  body.theme-minimal *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ===========================================================================
   Everything the first two pages never exercised.

   The rules above are written against the ordinary screens — a card, a table
   of a dozen rows, a dialog. The rest of the app has denser furniture than
   that, and some of it is measured: a payslip has to fit one sheet of paper,
   and the run table has to fit its panel or the frozen header stops working.
   So the roomy padding above is put back to what those screens had.
   =========================================================================== */

/* A payslip is printed sixty at a time, one to a page. Three more pixels a row
   is most of a centimetre down a slip, which is how a slip ends up on two
   sheets. The run table (#payslipsWrap) is already held by its own id. */
body.theme-minimal .slip th,
body.theme-minimal .slip td { padding: 10px 12px; }
body.theme-minimal .slip tr.sub-row td { padding-top: 4px; padding-bottom: 4px; }
body.theme-minimal .slip { border: 1px solid var(--border); border-radius: 18px; }
/* ...but not when it is already sitting on a dialog's own white card. The rule
   above overrode style.css's `.modal .slip { border: none }` and put a box
   inside a box. */
body.theme-minimal .modal .slip { border: none; border-radius: 0; }
body.theme-minimal .slip-header { border-bottom-width: 1px; }

/* Tables meant to read one record per line: the loans list carries twelve
   columns, and the extra padding is what would push them into wrapping. */
body.theme-minimal table.one-line th,
body.theme-minimal table.one-line td { padding: 9px 10px; }
body.theme-minimal .bill-card th,
body.theme-minimal .bill-card td { padding: 7px 8px; }
body.theme-minimal .mapping-table th,
body.theme-minimal .mapping-table td { padding: 8px 10px; }

/* ---- Sign in ----------------------------------------------------------- */
body.theme-minimal .login-shell { background: var(--bg); }
body.theme-minimal .login-card {
  border-top: none;
  border-radius: 20px;
  padding: 40px 38px 34px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04), 0 22px 55px rgba(0, 0, 0, 0.09);
  animation: tmRise 0.2s cubic-bezier(0.32, 0.72, 0, 1) both;
}
body.theme-minimal .login-card h1 { font-size: 24px; }

/* ---- The employee's own page, opened on a phone ------------------------ */
body.theme-minimal .portal-head { border-bottom: 1px solid var(--border); }
body.theme-minimal .portal-card { border-radius: var(--radius); }
body.theme-minimal .portal-card:hover { border-color: transparent; }
body.theme-minimal .portal-month { font-weight: 500; }
body.theme-minimal .portal-net strong { letter-spacing: -0.02em; }
body.theme-minimal .portal-net-label { text-transform: none; letter-spacing: -0.01em; font-size: 12px; }
body.theme-minimal .portal-empty { border: none; border-radius: 18px; box-shadow: var(--shadow); }

/* ---- The furniture each page brings with it ---------------------------- */
body.theme-minimal .owed-card {
  border: none;
  border-radius: var(--radius);
  background: var(--panel);
  box-shadow: var(--shadow);
}
body.theme-minimal .owed-card .label { text-transform: none; letter-spacing: -0.01em; font-size: 13px; }
body.theme-minimal .owed-card .value { font-weight: 600; letter-spacing: -0.02em; }
body.theme-minimal .owed-card.accent { background: var(--primary-soft); border: none; }
body.theme-minimal .hist-grid > div { border: none; border-radius: 12px; background: var(--bg); }
body.theme-minimal .bulk-bar,
body.theme-minimal .skip-box,
body.theme-minimal .ot-checks,
body.theme-minimal .panel-note { border: none; border-radius: 12px; }
body.theme-minimal .suggest-box {
  border: none;
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.14);
}
body.theme-minimal .suggest-item:first-child { border-radius: 12px 12px 0 0; }
body.theme-minimal .suggest-item:last-child { border-radius: 0 0 12px 12px; }
body.theme-minimal .picker > .picker-clear:hover { background: rgba(0, 0, 0, 0.07); }
html[data-theme="dark"] body.theme-minimal .picker > .picker-clear:hover { background: rgba(255, 255, 255, 0.12); }
/* Inside a dialog the panel floats over a blurred scrim, so it needs the
   heavier shadow the modals use rather than the flat card treatment. */
body.theme-minimal .picker-panel { box-shadow: var(--shadow-lg, 0 18px 50px rgba(0, 0, 0, 0.22)); }
body.theme-minimal .bill-card { border-radius: var(--radius); }
body.theme-minimal .chip-sunday { border-radius: 980px; }
body.theme-minimal th.sortable:hover { color: var(--primary); }

/* ---- On paper ----------------------------------------------------------
   Shadows print as grey smudges and an animation has no meaning on a sheet
   of paper. */
@media print {
  body.theme-minimal { background: #fff; }
  body.theme-minimal .panel,
  body.theme-minimal .stat-card,
  body.theme-minimal .slip,
  body.theme-minimal .portal-card,
  body.theme-minimal .owed-card,
  body.theme-minimal .bill-card {
    box-shadow: none !important;
    animation: none !important;
  }
}

/* A button label never wraps: "+ New Loan" broke across two lines inside its
   pill the moment the pill was wider than the label. */
body.theme-minimal .btn { white-space: nowrap; }

/* The file pickers on the import step are the one control the browser draws
   itself. Given the same pill so the step doesn't have a 1995 button in it. */
body.theme-minimal input[type=file] { font-size: 13.5px; color: var(--text-muted); }
body.theme-minimal input[type=file]::file-selector-button {
  border: none;
  border-radius: 980px;
  padding: 8px 16px;
  margin-right: 12px;
  background: rgba(0, 0, 0, 0.05);
  color: var(--text);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 0.2s ease;
}
body.theme-minimal input[type=file]::file-selector-button:hover { background: rgba(0, 0, 0, 0.09); }

/* Disabled at 0.4 read as broken rather than "not yet". */
body.theme-minimal .btn:disabled { opacity: 0.45; }

/* ===========================================================================
   Second pass — the things the browser was still drawing for us, and the
   places where a pill was the wrong shape.
   =========================================================================== */

/* ---- The brand, once ---------------------------------------------------
   The bar used to carry the logo (which IS the wordmark) and then the
   company name again in text beside it. One mark, one product name. */
body.theme-minimal .brand-mark { height: 26px; width: 26px; display: block; flex-shrink: 0; }
body.theme-minimal .topnav .brand { gap: 12px; }
/* The rule goes AFTER the product name, between the masthead and the links —
   putting it before "Payroll" separated the word from the mark it belongs to
   and left it hard against "Dashboard", which is what made it read as the
   first nav item in the first place. */
body.theme-minimal .topnav .brand-text {
  flex-direction: row;
  align-items: baseline;
  gap: 7px;
  padding-right: 14px;
  border-right: 1px solid var(--border);
}
/* No more shouting: it was uppercase with letter-spacing, which is the one
   thing this project's own conventions say not to do. */
body.theme-minimal .topnav .brand-text .sub {
  color: var(--text-muted);
  opacity: 1;
  font-size: 13px;
  text-transform: none;
  letter-spacing: -0.01em;
}

/* ---- Select: our chevron, not the platform's --------------------------- */
body.theme-minimal select {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1.5 1.75 6 6.25l4.5-4.5' stroke='%236e6e73' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
  background-size: 12px 8px;
}
body.theme-minimal select::-ms-expand { display: none; }

/* ---- Checkbox: rounded, filled, with a real tick ----------------------- */
body.theme-minimal input[type=checkbox] {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  margin: 0;
  border: 1px solid rgba(0, 0, 0, 0.22);
  border-radius: 5px;
  background: #fff;
  flex-shrink: 0;
  cursor: pointer;
  vertical-align: middle;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
body.theme-minimal input[type=checkbox]:checked {
  background-color: var(--primary);
  border-color: var(--primary);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='10' viewBox='0 0 12 10' fill='none'%3E%3Cpath d='M1.5 5.2 4.4 8 10.5 1.8' stroke='white' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
}
body.theme-minimal input[type=checkbox]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.22);
}

/* Overtime entitlement is a setting that is on or off for a person, which is
   what a switch is for. Same input, drawn as one. */
body.theme-minimal .ot-checks input[type=checkbox] {
  width: 42px;
  height: 25px;
  border-radius: 980px;
  border: none;
  background: rgba(0, 0, 0, 0.14);
  background-image: none;
  position: relative;
  transition: background-color 0.28s cubic-bezier(0.32, 0.72, 0, 1);
}
body.theme-minimal .ot-checks input[type=checkbox]::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
  transition: transform 0.28s cubic-bezier(0.32, 0.72, 0, 1);
}
body.theme-minimal .ot-checks input[type=checkbox]:checked {
  background-color: var(--primary);
  background-image: none;
}
body.theme-minimal .ot-checks input[type=checkbox]:checked::after { transform: translateX(17px); }
body.theme-minimal .ot-checks label { gap: 12px; }

/* ---- Dates: the value was rendering in a monospace face ---------------- */
body.theme-minimal input[type=date],
body.theme-minimal input[type=month] { font-family: inherit; font-variant-numeric: normal; }
body.theme-minimal input[type=date]::-webkit-datetime-edit,
body.theme-minimal input[type=month]::-webkit-datetime-edit {
  font-family: inherit;
  font-variant-numeric: normal;
}
body.theme-minimal input[type=date]::-webkit-calendar-picker-indicator,
body.theme-minimal input[type=month]::-webkit-calendar-picker-indicator {
  opacity: 0.45;
  cursor: pointer;
  transition: opacity 0.15s ease;
}
body.theme-minimal input[type=date]::-webkit-calendar-picker-indicator:hover,
body.theme-minimal input[type=month]::-webkit-calendar-picker-indicator:hover { opacity: 0.8; }
/* The label beside a month picker was wrapping onto two lines. */
body.theme-minimal .month-field { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
body.theme-minimal .month-field label { margin: 0; white-space: nowrap; }
body.theme-minimal .month-field input { width: auto; }
/* What the month being shown means for the payroll run it feeds. It sits
   under the picker rather than beside it, because it is a sentence and not a
   field, and it takes the whole row so the header above it keeps its shape. */
body.theme-minimal .month-note { flex-basis: 100%; margin-top: 8px; display: flex; justify-content: flex-end; }
body.theme-minimal .month-note:empty { display: none; }
body.theme-minimal .month-flag {
  max-width: 360px;
  padding: 7px 12px;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.04);
  color: var(--text-muted);
  font-size: 12.5px;
  line-height: 1.45;
  text-align: left;
}
/* A month whose run is already closed is the one thing worth colouring: the
   work still saves, it simply no longer reaches the salaries. */
body.theme-minimal .month-flag.warn { background: var(--danger-bg); color: var(--danger); }
body.theme-minimal .month-flag.warn a { color: var(--danger); text-decoration: underline; }
html[data-theme="dark"] body.theme-minimal .month-flag { background: rgba(255, 255, 255, 0.06); }

/* ---- A pill is for a button that hugs its label ------------------------
   Stretched across a card it becomes a lozenge, which is not a shape Apple
   uses. A button that spans its container gets a modest corner instead. */
body.theme-minimal .btn.block {
  border-radius: 12px;
  width: 100%;
  justify-content: center;
  padding: 12px 20px;
  font-size: 15px;
}

/* A destructive action repeated on every row of a table should not be the
   loudest thing on the screen. Red, but as a tint rather than a slab. */
body.theme-minimal .btn.danger.small {
  background: transparent;
  color: var(--danger);
}
body.theme-minimal .btn.danger.small:hover { background: var(--danger-bg); }

/* Disabled should read as grey-not-yet, not as a faded blue. */
body.theme-minimal .btn:disabled {
  opacity: 1;
  background: rgba(0, 0, 0, 0.06);
  color: rgba(0, 0, 0, 0.32);
}

/* ---- An icon where there was nothing ---------------------------------- */
body.theme-minimal .btn .ico { width: 15px; height: 15px; flex-shrink: 0; }
body.theme-minimal .btn.small .ico { width: 13px; height: 13px; }

/* Sort direction as a chevron rather than a black triangle. */
body.theme-minimal th.sortable .arrow {
  font-size: 9px;
  opacity: 0.3;
  vertical-align: middle;
  margin-left: 2px;
}
body.theme-minimal th.sortable.sorted .arrow { opacity: 0.75; }

/* An empty state is a moment, not a blank page: a symbol, then the sentence. */
body.theme-minimal .empty-state {
  padding: 34px 20px 36px;
  font-size: 14.5px;
}
body.theme-minimal .empty-state::before {
  content: "";
  display: block;
  width: 28px;
  height: 28px;
  margin: 0 auto 12px;
  opacity: 0.32;
  background: no-repeat center / contain
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='34' height='34' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 14h4l1.5 2.5h7L17 14h4'/%3E%3Cpath d='M5.5 5.5h13L21 14v3.5a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 17.5V14z'/%3E%3C/svg%3E");
}

/* ---- The run table: a figure, not a box, until you are in it ----------
   Sixty rows of chunky outlined fields reads as a form. The value sits
   plainly on the row and the field appears where the cursor is. */
body.theme-minimal #payslipsWrap input.editf {
  border-color: transparent;
  background: rgba(0, 0, 0, 0.025);
  border-radius: 8px;
  transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
body.theme-minimal #payslipsWrap input.editf:hover { background: rgba(0, 0, 0, 0.05); }
body.theme-minimal #payslipsWrap input.editf:focus {
  background: #fff;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.18);
}

/* Names in the staff list were wrapping onto four lines. */
body.theme-minimal td.name-cell, body.theme-minimal th.name-cell { min-width: 160px; }

/* The filter bar's Clear button sat lower than the fields beside it. */
body.theme-minimal .filter-bar > .btn { align-self: flex-end; }

/* An icon-only button is a circle the size of a comfortable target, not a
   pill with nothing in it. */
body.theme-minimal .btn.icon-only {
  width: 30px;
  height: 30px;
  padding: 0;
  justify-content: center;
}
body.theme-minimal .btn.icon-only .ico { width: 16px; height: 16px; }
/* Sixty-three green discs down one column was the loudest thing on the Staff
   page, and with the Edit pencil gone it was the only thing left shouting.
   The glyph keeps WhatsApp's green — it is how the mark is recognised — and
   loses the disc, so it matches the Copy icon beside it and the fill only
   appears under the cursor. Outside a table a worded WhatsApp button is still
   a filled pill; there it is the action of the panel, not one of sixty. */
body.theme-minimal table .btn.wa.icon-only {
  background: transparent; border-color: transparent; color: #1da851;
}
html[data-theme="dark"] body.theme-minimal table .btn.wa.icon-only { color: #25d366; }
body.theme-minimal table .btn.wa.icon-only:hover,
body.theme-minimal table .btn.wa.icon-only:focus-visible {
  background: #25d366; border-color: #25d366; color: #fff;
}
body.theme-minimal .btn.wa.icon-only { background: #25d366; border-color: #25d366; color: #fff; }
body.theme-minimal .btn.wa.icon-only:hover { background: #1fb457; border-color: #1fb457; }

/* ===========================================================================
   Appearance: light and dark.

   Dark is not the light theme inverted. The blue on a button is the same blue,
   a status still means what it meant, and the company's colours are untouched.
   What changes is the paper: white becomes a dark grey, and the cards sit one
   step above the page instead of one step below the light.
   =========================================================================== */

/* ---- The switch: a sun, a moon, and a knob that slides between them ----- */
body.theme-minimal .theme-switch {
  position: relative;
  width: 54px;
  height: 30px;
  flex-shrink: 0;
  padding: 0;
  border: none;
  border-radius: 980px;
  background: rgba(0, 0, 0, 0.09);
  cursor: pointer;
  transition: background-color 0.3s cubic-bezier(0.32, 0.72, 0, 1);
}
body.theme-minimal .theme-knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.22);
  transition: transform 0.32s cubic-bezier(0.32, 0.72, 0, 1);
  z-index: 1;
}
body.theme-minimal .theme-ico {
  position: absolute;
  top: 8px;
  width: 14px;
  height: 14px;
  z-index: 2;
  transition: color 0.3s ease, opacity 0.3s ease;
  pointer-events: none;
}
body.theme-minimal .theme-sun { left: 8px; color: var(--text); opacity: 1; }
body.theme-minimal .theme-moon { right: 8px; color: var(--text-muted); opacity: 0.45; }
body.theme-minimal .theme-switch[aria-checked="true"] .theme-knob { transform: translateX(24px); }
body.theme-minimal .theme-switch[aria-checked="true"] .theme-sun { opacity: 0.45; color: var(--text-muted); }
body.theme-minimal .theme-switch[aria-checked="true"] .theme-moon { opacity: 1; color: #1d1d1f; }
body.theme-minimal .theme-switch:active .theme-knob { transform: scale(0.94); }
body.theme-minimal .theme-switch[aria-checked="true"]:active .theme-knob { transform: translateX(24px) scale(0.94); }

/* Sign in, not-found and the employee's own page have no bar to sit in. */
body.theme-minimal .theme-float { position: fixed; top: 18px; right: 18px; z-index: 60; }

/* Both logos are in the page; the appearance decides which one is drawn. */
body.theme-minimal .on-dark { display: none; }
html[data-theme="dark"] body.theme-minimal .on-light { display: none; }
html[data-theme="dark"] body.theme-minimal .on-dark { display: block; }
html[data-theme="dark"] body.theme-minimal .slip-header .on-dark { display: block; }

/* ---- The dark surfaces -------------------------------------------------- */
html[data-theme="dark"] { color-scheme: dark; background: #151517; }
html[data-theme="dark"] body.theme-minimal {
  --bg: #151517;
  --bg-alt: #2a2a2e;
  --panel: #202023;
  --border: rgba(255, 255, 255, 0.11);
  --text: #f5f5f7;
  --text-muted: #a1a1a6;

  /* untouched: the same blue on the same buttons */
  --primary: #0071e3;
  --primary-dark: #0077ed;
  --accent: #409cff;           /* links and dots only — the fill stays #0071e3 */
  --primary-soft: #17293f;
  --danger: #d70015;           /* filled destructive buttons, unchanged */
  --danger-text: #ff6b6b;      /* the same red, lifted enough to read on dark */
  --danger-bg: #3a1c1c;
  --success-bg: #14301f;

  /* Apple's dark sections lift the link blue rather than keeping #0066cc,
     which goes muddy on near-black. The button fill does not move. */
  --link: #2997ff;

  --shadow: none;
  --shadow-lg: 0 28px 70px rgba(0, 0, 0, 0.62);
}

html[data-theme="dark"] body.theme-minimal .topnav { background: rgba(21, 21, 23, 0.72); }
html[data-theme="dark"] body.theme-minimal .topnav-links a:hover,
html[data-theme="dark"] body.theme-minimal .topnav-right button:hover { background: rgba(255, 255, 255, 0.09); }
html[data-theme="dark"] body.theme-minimal .topnav-links a.active { background: rgba(255, 255, 255, 0.12); }
html[data-theme="dark"] body.theme-minimal .nav-group-btn:hover { background: rgba(255, 255, 255, 0.09); }
html[data-theme="dark"] body.theme-minimal .nav-group-btn.active,
html[data-theme="dark"] body.theme-minimal .nav-group-btn.is-open { background: rgba(255, 255, 255, 0.12); }
html[data-theme="dark"] body.theme-minimal .nav-menu { box-shadow: 0 12px 34px rgba(0, 0, 0, 0.5); }
html[data-theme="dark"] body.theme-minimal .topnav-right button { background: rgba(255, 255, 255, 0.09); color: var(--text); }

/* A card is a step above the page here, not a white slab. */
html[data-theme="dark"] body.theme-minimal .panel,
html[data-theme="dark"] body.theme-minimal .stat-card,
html[data-theme="dark"] body.theme-minimal .portal-card,
html[data-theme="dark"] body.theme-minimal .bill-card,
html[data-theme="dark"] body.theme-minimal .owed-card,
html[data-theme="dark"] body.theme-minimal .modal,
html[data-theme="dark"] body.theme-minimal .login-card {
  background: var(--panel);
  border: 1px solid rgba(255, 255, 255, 0.07);
}
html[data-theme="dark"] body.theme-minimal .slip { background: var(--panel); border-color: rgba(255, 255, 255, 0.07); }
html[data-theme="dark"] body.theme-minimal #payslipsWrap tbody tr { background: transparent; }
html[data-theme="dark"] body.theme-minimal table thead th { background: var(--panel); }
html[data-theme="dark"] body.theme-minimal table th,
html[data-theme="dark"] body.theme-minimal table td { border-bottom-color: rgba(255, 255, 255, 0.08); }
html[data-theme="dark"] body.theme-minimal table tbody tr:hover { background: rgba(255, 255, 255, 0.07); }

html[data-theme="dark"] body.theme-minimal .btn.secondary { background: rgba(255, 255, 255, 0.11); color: var(--text); }
html[data-theme="dark"] body.theme-minimal .btn.secondary:hover { background: rgba(255, 255, 255, 0.16); }
html[data-theme="dark"] body.theme-minimal .btn:disabled { background: rgba(255, 255, 255, 0.08); color: rgba(255, 255, 255, 0.34); }
html[data-theme="dark"] body.theme-minimal .btn.danger.small { color: var(--danger-text); }
html[data-theme="dark"] body.theme-minimal .btn.danger.small:hover { background: var(--danger-bg); }
html[data-theme="dark"] body.theme-minimal .error-text { color: var(--danger-text); }

html[data-theme="dark"] body.theme-minimal input[type=text],
html[data-theme="dark"] body.theme-minimal input[type=number],
html[data-theme="dark"] body.theme-minimal input[type=email],
html[data-theme="dark"] body.theme-minimal input[type=password],
html[data-theme="dark"] body.theme-minimal input[type=date],
html[data-theme="dark"] body.theme-minimal input[type=month],
html[data-theme="dark"] body.theme-minimal input[type=tel],
html[data-theme="dark"] body.theme-minimal select,
html[data-theme="dark"] body.theme-minimal textarea {
  /* background-COLOR, not the shorthand: the shorthand resets repeat, position
     and size, which tiled the select's chevron right across the field. */
  background-color: #2a2a2e;
  border-color: rgba(255, 255, 255, 0.14);
  color: var(--text);
}
html[data-theme="dark"] body.theme-minimal select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1.5 1.75 6 6.25l4.5-4.5' stroke='%23a1a1a6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
html[data-theme="dark"] body.theme-minimal input[type=checkbox] {
  background: transparent;
  border-color: rgba(255, 255, 255, 0.3);
}
html[data-theme="dark"] body.theme-minimal input[type=checkbox]:checked {
  background-color: var(--primary);
  border-color: var(--primary);
}
html[data-theme="dark"] body.theme-minimal .ot-checks input[type=checkbox] { background: rgba(255, 255, 255, 0.18); }
html[data-theme="dark"] body.theme-minimal .ot-checks input[type=checkbox]:checked { background: var(--primary); }
html[data-theme="dark"] body.theme-minimal input[type=date]::-webkit-calendar-picker-indicator,
html[data-theme="dark"] body.theme-minimal input[type=month]::-webkit-calendar-picker-indicator { filter: invert(1); }
html[data-theme="dark"] body.theme-minimal input[type=file] { color: var(--text-muted); }
html[data-theme="dark"] body.theme-minimal input[type=file]::file-selector-button {
  background: rgba(255, 255, 255, 0.11);
  color: var(--text);
}
html[data-theme="dark"] body.theme-minimal input[type=file]::file-selector-button:hover { background: rgba(255, 255, 255, 0.16); }

/* Status keeps its meaning; only the shade lifts enough to read on dark. */
html[data-theme="dark"] body.theme-minimal .badge.active,
html[data-theme="dark"] body.theme-minimal .badge.finalized { color: #30d158; }
html[data-theme="dark"] body.theme-minimal .badge.draft { color: #ffc53d; }

html[data-theme="dark"] body.theme-minimal .modal-backdrop { background: rgba(0, 0, 0, 0.55); }
html[data-theme="dark"] body.theme-minimal .modal-close { background: rgba(255, 255, 255, 0.1); }
html[data-theme="dark"] body.theme-minimal .modal-close:hover { background: rgba(255, 255, 255, 0.16); }
html[data-theme="dark"] body.theme-minimal .suggest-box { background: var(--bg-alt); }
html[data-theme="dark"] body.theme-minimal .suggest-item:hover,
html[data-theme="dark"] body.theme-minimal .suggest-item.active { background: rgba(255, 255, 255, 0.09); }
html[data-theme="dark"] body.theme-minimal .toast { background: rgba(58, 58, 62, 0.92); border: 1px solid rgba(255, 255, 255, 0.1); }
html[data-theme="dark"] body.theme-minimal .hist-grid > div,
html[data-theme="dark"] body.theme-minimal .skip-box { background: rgba(255, 255, 255, 0.05); }
html[data-theme="dark"] body.theme-minimal .theme-switch { background: rgba(255, 255, 255, 0.16); }
html[data-theme="dark"] body.theme-minimal #payslipsWrap input.editf { background: rgba(255, 255, 255, 0.07); }
html[data-theme="dark"] body.theme-minimal #payslipsWrap input.editf:hover { background: rgba(255, 255, 255, 0.12); }
html[data-theme="dark"] body.theme-minimal #payslipsWrap input.editf:focus { background: #2a2a2e; }

/* Rows that carry a meaning of their own. */
html[data-theme="dark"] body.theme-minimal table tbody tr.row-edited,
html[data-theme="dark"] body.theme-minimal table tbody tr.row-edited:hover { background: rgba(255, 197, 61, 0.13); }
html[data-theme="dark"] body.theme-minimal table tbody tr.row-added,
html[data-theme="dark"] body.theme-minimal table tbody tr.row-added:hover { background: rgba(48, 209, 88, 0.13); }
html[data-theme="dark"] body.theme-minimal tr.unmatched-row { background: rgba(255, 197, 61, 0.1); }

/* Paper is white whatever the screen is doing. */
@media print {
  /* Paper is white whatever the screen is doing — but naming elements one at a
     time is how three header rows printed as solid black bars. They paint
     `var(--panel)`, which is near-black in dark mode, and nothing in this block
     had listed them. Reset the VARIABLES instead: every element follows at
     once, and anything added to a payslip later cannot come out redacted. */
  html[data-theme="dark"] body.theme-minimal {
    --bg: #ffffff;
    --bg-alt: #ffffff;
    --panel: #ffffff;
    --text: #1d1d1f;
    --text-muted: #6e6e73;
    --border: rgba(0, 0, 0, 0.12);
    --primary-soft: #eaf3fd;
    --danger-bg: #ffeceb;
    --success-bg: #e8f8ee;
    background: #fff !important;
    color: #1d1d1f !important;
  }
  html[data-theme="dark"] body.theme-minimal .panel,
  html[data-theme="dark"] body.theme-minimal .slip,
  html[data-theme="dark"] body.theme-minimal .modal { background: #fff !important; color: #1d1d1f !important; }
  html[data-theme="dark"] body.theme-minimal .on-dark { display: none !important; }
  html[data-theme="dark"] body.theme-minimal .on-light { display: block !important; }
  html[data-theme="dark"] body.theme-minimal .slip th,
  html[data-theme="dark"] body.theme-minimal .slip td,
  html[data-theme="dark"] body.theme-minimal .slip .hint { color: #1d1d1f !important; }

  /* A4 landscape with this app's margins leaves 711px. The slip was 748 and
     the page carried another 120px of screen padding on top, so every payslip
     spilled a blank second sheet — and a run of sixty printed sixty-one. The
     padding rule in style.css is outranked by the theme's own, so it is
     restated here, and the slip takes tighter metrics on paper than on screen.
     Measured after: 649px, one sheet per payslip. */
  body.theme-minimal .page-wrap { padding-top: 0; padding-bottom: 0; }
  body.theme-minimal .slip { font-size: 13px; line-height: 1.34; }
  body.theme-minimal .slip table { line-height: 17px; }
  body.theme-minimal .slip .hint { line-height: 14px; }
  body.theme-minimal .slip th,
  body.theme-minimal .slip td { padding-top: 7px; padding-bottom: 7px; }
}

/* ===========================================================================
   The keyboard, the narrow window, and the symbols that meant nothing.
   =========================================================================== */

/* ---- Focus you can see ------------------------------------------------
   There was no focus style anywhere but the checkboxes, so tabbing fell back
   to the browser's own outline — which, on a pill, draws as two black arcs
   either side of the word and reads as a rendering fault. A modern outline
   follows the border radius, so one rule covers every shape in the app. */
body.theme-minimal :focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: 2px;
}
/* The link strip scrolls sideways, and a scroll container clips anything
   painted outside it — which sliced the top and bottom off the ring and left
   two arcs either side of the word. Inside the pill it cannot be clipped. */
body.theme-minimal .topnav-links a:focus-visible,
body.theme-minimal .nav-group-btn:focus-visible { outline-offset: -3px; }

/* The fields already answer focus with a ring of their own. */
body.theme-minimal input:focus-visible,
body.theme-minimal select:focus-visible,
body.theme-minimal textarea:focus-visible { outline: none; }
body.theme-minimal input[type=checkbox]:focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: 2px;
  box-shadow: none;
}

/* ---- The bar on a narrower window --------------------------------------
   Eight sections, a masthead and an account cluster stop fitting somewhere
   around a small laptop. The links tighten first; below that the strip
   scrolls, and `is-scrollable` (set in nav.js by measurement, not by a
   guessed breakpoint) fades its trailing edge so it is visible that there
   is more to reach. */
@media (max-width: 1320px) {
  body.theme-minimal .topnav-links a,
  body.theme-minimal .nav-group-btn { padding: 7px 10px; font-size: 13px; }
  body.theme-minimal .topnav { padding: 0 18px; }
}
@media (max-width: 1150px) {
  body.theme-minimal .topnav-links a,
  body.theme-minimal .nav-group-btn { padding: 7px 8px; font-size: 12.5px; }
  body.theme-minimal .brand-logo { height: 30px; }
}
body.theme-minimal .topnav-links.is-scrollable {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
  mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
}

/* Appearance is a setting; a name and a way out are something else. A rule
   between them stops the three reading as one cluster. */
body.theme-minimal .topnav-sep {
  width: 1px;
  height: 20px;
  background: var(--border);
  flex-shrink: 0;
}

/* ---- Selection --------------------------------------------------------- */
body.theme-minimal ::selection { background: rgba(0, 113, 227, 0.22); }
html[data-theme="dark"] body.theme-minimal ::selection { background: rgba(64, 156, 255, 0.3); }

/* ---- An empty state says what is missing -------------------------------
   One tray for everything meant the symbol carried no information — and on
   the Vehicles page the same tray appeared twice on one screen, once for
   charges and once for vehicles. Each kind now has its own. */
body.theme-minimal .empty-state.people::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='9' cy='8' r='3.4'/%3E%3Cpath d='M2.6 19.6a6.6 6.6 0 0 1 12.8 0'/%3E%3Cpath d='M16.5 5.6a3.4 3.4 0 0 1 0 6.6M18 14.4a6.6 6.6 0 0 1 3.4 5.2'/%3E%3C/svg%3E");
}
body.theme-minimal .empty-state.runs::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='3' width='16' height='18' rx='2.4'/%3E%3Cpath d='M8 8h8M8 12h8M8 16h5'/%3E%3C/svg%3E");
}
body.theme-minimal .empty-state.money::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.5' y='6' width='19' height='12' rx='2.4'/%3E%3Ccircle cx='12' cy='12' r='2.6'/%3E%3Cpath d='M6 12h.01M18 12h.01'/%3E%3C/svg%3E");
}
body.theme-minimal .empty-state.bills::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.5 3h13v18l-2.2-1.6-2.1 1.6-2.2-1.6-2.1 1.6L7.7 19.4 5.5 21z'/%3E%3Cpath d='M9 8h6M9 12h6'/%3E%3C/svg%3E");
}
/* A SIM is a phone; a bill is a receipt. They were the same picture. */
body.theme-minimal .empty-state.sim::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='6' y='2.5' width='12' height='19' rx='2.6'/%3E%3Cpath d='M10.5 18.5h3'/%3E%3C/svg%3E");
}
body.theme-minimal .empty-state.vehicles::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 16.5v-3l1.8-4.2A2.4 2.4 0 0 1 7 7.8h10a2.4 2.4 0 0 1 2.2 1.5L21 13.5v3'/%3E%3Cpath d='M3 16.5h18'/%3E%3Ccircle cx='7.2' cy='16.8' r='1.7'/%3E%3Ccircle cx='16.8' cy='16.8' r='1.7'/%3E%3C/svg%3E");
}
body.theme-minimal .empty-state.search::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6.5'/%3E%3Cpath d='M15.4 15.4 21 21'/%3E%3C/svg%3E");
}
html[data-theme="dark"] body.theme-minimal .empty-state::before { opacity: 0.45; filter: invert(1) brightness(1.6); }

/* ---- Sort direction, in the same family as every other glyph ------------ */
body.theme-minimal th.sortable .arrow svg { width: 9px; height: 9px; display: block; }
body.theme-minimal th.sortable .arrow {
  display: inline-flex;
  align-items: center;
  margin-left: 4px;
  opacity: 0.3;
  color: currentColor;
  vertical-align: middle;
}
body.theme-minimal th.sortable.sorted .arrow { opacity: 0.8; }

/* The portal's switch belongs in its header, not floating over the payslip
   list on a phone. It sits at the end of the logo's row (.portal-bar, a flex
   row at every width). It used to be pinned to the header's top-right corner,
   and on a wide screen, where the name sat beside the logo, a long name ran
   straight underneath it. */
body.theme-minimal .portal-bar .theme-switch { margin: 0 0 0 auto; flex-shrink: 0; }

/* ---- One row, one shape ------------------------------------------------
   The Staff table ended with two round icon buttons and then a worded pill,
   so three controls that do the same sort of job were three different
   shapes. Anything living in a table row is now the same size and the same
   silhouette, whether it carries a word or a glyph. */
body.theme-minimal table .btn.small {
  min-height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* The dense one-line tables (payroll runs, payslips) had their own smaller
   pill — 22px against everyone else's 29. Same height as the rest now; the
   padding stays tighter so the table does not grow sideways. */
body.theme-minimal table.one-line .btn.small { padding: 5px 12px; }
body.theme-minimal .btn.small.icon-only { min-height: 30px; width: 30px; }
/* Several documents: the clip widens into a pill with its count, rather than
   squeezing a digit into a 30px circle. Same height, same radius. */
body.theme-minimal .btn.small.icon-only.paperclip.has-count { width: auto; padding: 0 10px 0 8px; }

/* A filled grey disc behind every glyph, repeated down sixty rows, stacks with
   the row's own hover tint and then with the button's own hover — three greys
   in one cell. The glyph is enough on its own; the disc appears where the
   cursor or the keyboard actually is. The 30px box and the pill radius do not
   move, so "one row, one shape" still holds. The WhatsApp green keeps its
   fill: there it marks at a glance who has a number on file. */
body.theme-minimal table .btn.secondary.icon-only {
  background: transparent;
  color: var(--text-muted);
}
body.theme-minimal table .btn.secondary.icon-only:hover,
body.theme-minimal table .btn.secondary.icon-only:focus-visible {
  background: rgba(0, 0, 0, 0.06);
  color: var(--text);
}
html[data-theme="dark"] body.theme-minimal table .btn.secondary.icon-only:hover,
html[data-theme="dark"] body.theme-minimal table .btn.secondary.icon-only:focus-visible {
  background: rgba(255, 255, 255, 0.12);
}

/* ---- A file chooser that belongs to the rest of the page ---------------
   The four upload fields were raw browser chrome: a grey system button and
   "No file chosen" in whatever the OS felt like. It was the only untouched
   control left in the app. */
body.theme-minimal input[type="file"] {
  width: auto;
  max-width: 100%;
  padding: 0;
  border: none;
  background: none;
  font-size: 13px;
  color: var(--text-muted);
  cursor: pointer;
}
body.theme-minimal input[type="file"]::file-selector-button {
  font: inherit;
  font-weight: 500;
  font-size: 13px;
  color: var(--text);
  background: rgba(0, 0, 0, 0.05);
  border: none;
  border-radius: 980px;
  padding: 8px 16px;
  margin-right: 12px;
  cursor: pointer;
  transition: background 0.18s ease, transform 0.18s cubic-bezier(0.32, 0.72, 0, 1);
}
body.theme-minimal input[type="file"]::file-selector-button:hover {
  background: rgba(0, 0, 0, 0.09);
}
body.theme-minimal input[type="file"]::file-selector-button:active { transform: scale(0.97); }
html[data-theme="dark"] body.theme-minimal input[type="file"]::file-selector-button {
  background: rgba(255, 255, 255, 0.11);
  color: var(--text);
}
html[data-theme="dark"] body.theme-minimal input[type="file"]::file-selector-button:hover {
  background: rgba(255, 255, 255, 0.17);
}

/* ---- The account cluster lines up ------------------------------------- */
body.theme-minimal .topnav-right .theme-switch,
body.theme-minimal .topnav-right button { height: 30px; }

/* ---- The filter bar, in one place -------------------------------------
   Staff & Rates labelled its filters and Held Salary did not, so the same
   row of controls read as two different components. Shared now, and the
   rule lives here rather than being copied into each page's <style>. */
body.theme-minimal .filter-bar {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin-bottom: 14px;
}
body.theme-minimal .filter-field { display: flex; flex-direction: column; gap: 4px; position: relative; }
body.theme-minimal .filter-field.grow { flex: 1 1 280px; min-width: 220px; max-width: 360px; }
/* With six filters the search box shares the row; with two it stretched
   most of the panel. A search box has a sensible largest size. */
body.theme-minimal .filter-field label { margin: 0; font-size: 12px; color: var(--text-muted); }
body.theme-minimal .filter-field select,
body.theme-minimal .filter-field input { margin: 0; }

/* The action columns are sized by their buttons, so a two-word heading above
   them wrapped and made the whole header row taller. */
body.theme-minimal thead th { white-space: nowrap; }
