/**
 * ── The palette ─────────────────────────────────────────────── (LAYOUT-1) ──
 *
 * The standard look is Reckoner's, by the owner's choice: warm cream paper,
 * white panels, and navy for the text and for the pointing — buttons, links,
 * the focus ring, the selected module. The values are Reckoner's own design
 * tokens (`@solvagio/tokens`), read off its build and written here under this
 * file's names, so nothing is fetched and nothing else had to learn new names.
 *
 * It replaced the firm's green chrome and bronze accent. The rule that shaped
 * those still holds: **the accent is never green and never red**, because
 * `--good` is a green that means approved and `--bad` a red that means wrong.
 * Navy is neither.
 *
 * The status colours were left where they were. They are the one part of the
 * palette that carries meaning, and meaning is not restyled for a new look.
 *
 * Every pair below clears 4.5:1 against the surface it sits on — white on the
 * navy fill 15.6, navy on white 15.6, the muted text on white 7.6, on the cream
 * 6.9 — and `shot-appearance.mjs` measures them on screen.
 */
:root {
  /* The monogram's square. The bars themselves are panels now (LAYOUT-1), so
     the "rail" names are only the mark's ground and letters, and the themes in
     Settings → Appearance still give them a value of their own. */
  --rail: #0b2545;
  --rail-fg: #a8b0bf;
  --rail-fg-on: #ece6d8;
  --rail-link: #dde6f1;
  --hair: #c9b98f;

  /* Reckoner's surfaces: cream paper, white panels, a warm hairline. */
  --bg: #f5f1ea;
  --panel: #ffffff;
  --line: #e3ddd0;
  --line-strong: #cbc2ae;

  /**
   * ── The text's six strengths ─────────────────────────────── (LOOK-1) ──
   *
   * `--ink` and `--ink-soft` are what every rule reads — sixteen and sixty
   * times — and they are the top and the middle of a six-step scale the
   * colour theme sets (`--p-…`). Settings → Appearance moves the text a step
   * along that scale (`[data-text]`, below), which is only possible if there
   * IS a scale: with two values there is nowhere for "softer" to go that is
   * not already "muted".
   *
   * The two names that were here keep their exact values, as the first and
   * third steps, so the standard look did not move by one hex digit when the
   * scale arrived. The other four are the room on either side.
   */
  --p-ink: #0b2545;
  --p-ink-2: #24344f;
  --p-ink-soft: #4a5163;
  --p-muted: #5f6675;
  --p-muted-2: #787e8c;
  --p-faint: #8d93a0;
  --ink: var(--p-ink);
  --ink-soft: var(--p-ink-soft);

  /* The pointing. Reckoner's navy: never green, never red. */
  --accent: #0b2545;
  --accent-dark: #15355f;
  --accent-tint: #dde6f1;
  --accent-soft: #9db8da;
  --accent-soft-dark: #7f9fc8;

  /**
   * The accent as TEXT, apart from the accent as a FILL. (THEME-1)
   *
   * The same value here, and deliberately two names: a fill carries white text
   * and so cannot be lightened far, while text on a dark ground has to be. In
   * the light theme they are one colour and this costs nothing; at night they
   * part company, and a screen that used one name for both would have either
   * unreadable links or white-on-pale buttons. The accounting app splits them
   * the same way and for the same reason.
   */
  --accent-text: #0b2545;
  --accent-text-strong: #061a33;

  /**
   * The two off-whites. A hovered row, a totals row and the quiet side of a
   * window were each a different hand-mixed grey before, all of them cool —
   * which is how a warm page ends up with cold patches on it. Two values,
   * named, used everywhere.
   */
  --tint: #f7f4ee;
  --tint-soft: #fbf9f5;

  /* Meaning. Unchanged. */
  --bad: #9c2f2f;
  --bad-bg: #fbf1f1;
  --bad-line: #eccccc;
  --warn: #7d5a08;
  --warn-bg: #fbf7ea;
  --warn-line: #e8dcc0;
  --good: #1f7a4d;
  --good-bg: #eef7f1;
  --good-line: #cfe6da;

  /**
   * The greys that are not surfaces: a card's shadow, a dialog's backdrop, and
   * the warm cream a draft row goes on the way to being hovered. All three were
   * written out by hand where they were used, which is fine until the page
   * behind them turns dark and a shadow mixed for white paper disappears.
   */
  --shadow: rgba(11, 37, 69, .06);
  --backdrop: rgba(11, 37, 69, .45);
  --draft-hover: #efe7d6;

  /**
   * ── Type ──
   *
   * ── One face, Roboto, for everything ────────────────────── (ROBOTO-1) ──
   *
   * It was a serif for headings (Iowan Old Style, Palatino) and the machine's
   * own interface sans for the rest — so the application was drawn in a
   * different font on every computer, which is what FONT-1 had to work round.
   * Now Roboto, the face the Reckoner app is set in, for headings and figures
   * as well, by the owner's choice. It is served from THIS server
   * (`public/fonts/roboto.css`, linked before this file): nothing is fetched
   * from anywhere else — `services/security-headers.test.ts` still fails the
   * build on any external host — and every machine draws the same letters.
   * The system faces after it only stand in for a character Roboto's files do
   * not carry.
   */
  --sans: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", sans-serif;

  /**
   * ── The accents Settings → Appearance offers ──────────────── (LOOK-1) ──
   *
   * Each named ONCE and read twice: by the rule that makes it the accent when
   * somebody has chosen it, and by the swatch on the chooser. Two copies of a
   * colour is how the sample on the button comes to be a shade off the thing it
   * is a sample of, and nobody would ever notice which was wrong.
   *
   * Declared here and not again in the dark theme, like `--sans`
   * and for the same reason: these are not this theme's colours, they are the
   * fixed set of choices. The derivation that makes each one legible against
   * whatever theme is in force is below (`--accent-base`). `theme.test.ts`
   * names them with that reason.
   *
   * No green and no red. `services/appearance.ts` says why at length, and it
   * is this repository's own rule: green is chrome and already means approved,
   * red already means wrong.
   */
  --ac-teal: #2f5d5b;
  --ac-blue: #2d5a8c;
  --ac-indigo: #454d8f;
  --ac-ochre: #845612;
  --ac-plum: #6d3f6b;
  --ac-graphite: #454a52;
}

/**
 * ── The same application at night ─────────────────────────────────────────
 *
 * As the invoicing and accounting apps do it: `public/theme.js` sets
 * `data-theme` on the page BEFORE it is drawn — the person's choice in this
 * browser, or else the machine's — and everything below is the palette read
 * again under that attribute. Not one rule outside this block knows which
 * theme it is in, which is the whole reason the palette was worth having.
 *
 * Written by hand rather than filtered: warm grounds rather than black, text
 * short of white, and tinted backgrounds darkened far more than their text is
 * lightened. A `bad-bg` that was merely inverted comes out a glaring red.
 *
 * ON SCREEN ONLY. `@media screen` keeps every print value light, because a
 * payslip printed from a dark screen is still ink on white paper — and this
 * application exists to produce paper.
 *
 * The standard dark is Reckoner's night: deep navy panels on a slightly
 * lighter navy page, cream text. (LAYOUT-1)
 *
 * Two things do NOT move, and each for a reason:
 *
 *   · WHITE TEXT on the accent, the green and the red fills. Those fills stay
 *     dark enough to carry it, which is why `--accent` is a FILL colour here
 *     and `--accent-text` is the lightened one beside it.
 *   · the MEANING of the status colours. Red is still wrong, amber still
 *     wants attention, green still means approved — lightened to be read on a
 *     dark ground, never swapped.
 */
@media screen {
  :root[data-theme="dark"] {
    color-scheme: dark;

    /* The monogram's square, a step lighter than the bar it sits on. */
    --rail: #1c3a5e;
    --rail-fg: #a8b0bf;
    --rail-fg-on: #ece6d8;
    --rail-link: #c9d8ed;
    --hair: #c9b98f;

    /* Reckoner's night: the panels are the darker navy, the page between
       them a step lighter, which is what makes a panel read as a panel. */
    --bg: #15263f;
    --panel: #0b2545;
    --line: #1e3556;
    --line-strong: #2a3f60;

    /* The six strengths at night, lightest first — so "stronger" means
       LIGHTER here and the one rule serves both themes. The two values that
       were here are the first and third steps, unchanged. (LOOK-1) */
    --p-ink: #ece6d8;
    --p-ink-2: #d6d1c5;
    --p-ink-soft: #a8b0bf;
    --p-muted: #9ba3b2;
    --p-muted-2: #8a92a1;
    --p-faint: #747d8e;
    --ink: var(--p-ink);
    --ink-soft: var(--p-ink-soft);

    /* The fill stays dark enough for white text; the text is lightened. */
    --accent: #2d5486;
    --accent-dark: #24456f;
    --accent-tint: #1c3a5e;
    --accent-soft: #9db8da;
    --accent-soft-dark: #7f9fc8;
    --accent-text: #9db8da;
    --accent-text-strong: #c9d8ed;

    --tint: #10294a;
    --tint-soft: #132e52;

    /* Meaning, lightened to be legible — never swapped. */
    --bad: #f0908b;
    --bad-bg: #2b1817;
    --bad-line: #653230;
    --warn: #e0ab55;
    --warn-bg: #2a2114;
    --warn-line: #5f4c22;
    --good: #67cd91;
    --good-bg: #16261d;
    --good-line: #2f5a41;

    --shadow: rgba(0, 0, 0, .5);
    --backdrop: rgba(0, 0, 0, .7);
    --draft-hover: #1c3a5e;
  }

  /**
   * A button with no class of its own is drawn by the browser, in a light grey
   * that does not belong on this ground. `:where` gives this no weight at all,
   * so every rule the application writes still wins and only the browser's
   * default is replaced.
   */
  :where(:root[data-theme="dark"] button:not([class])) {
    background: var(--tint-soft); color: var(--ink);
    border: 1px solid var(--line-strong); border-radius: 6px;
  }
  :where(:root[data-theme="dark"] button:not([class]):hover:not(:disabled)) { background: var(--tint); }
}

/*
 * ── Settings → Appearance ───────────────────────────────────── (LOOK-1) ──
 *
 * One person's own look (`services/appearance.ts`): a colour theme, an accent
 * and the text's strength, each put on the html element as `data-palette`,
 * `data-accent` and `data-text` before the first paint by `public/theme.js`.
 *
 * **The standard look sets none of them**, which is what keeps Reckoner's
 * navy and cream exactly as they are (LAYOUT-1): `:root` above holds its literal
 * values and every rule in here needs an attribute to match. So this block
 * cannot change what anybody sees who has not chosen, and that is a property
 * of the selectors rather than a promise.
 *
 * Each theme has a light hand and a dark one, as the standard one does, and
 * gives values to the same names — `theme.test.ts` fails on a theme that
 * answers for fewer. Every combination is measured for contrast in both
 * themes by `tests/e2e/shot-appearance.mjs`.
 *
 * The status colours are NOT in here. Red still means wrong, amber still wants
 * attention, green still means approved, in every theme — and nothing in this
 * application is told by colour alone, which is what makes a palette a choice
 * rather than a risk.
 *
 * ON SCREEN ONLY, like the dark theme and for the same reason: a payslip
 * printed from a slate screen is still ink on white paper.
 */
@media screen {
  /* Cool blue-greys, a blue accent. */
  [data-palette="slate"][data-theme="light"] {
    --rail: #1f2a36; --rail-fg: #c3ccd6; --rail-link: #bdd2e8; --hair: #4a7ca8;
    --bg: #f3f5f8; --panel: #ffffff; --line: #dbe1e8; --line-strong: #c9d1db;
    --tint: #f1f4f7; --tint-soft: #f8fafc; --draft-hover: #e6eef7;
    --p-ink: #1b2430; --p-ink-2: #36414f; --p-ink-soft: #4a5563;
    --p-muted: #5d6877; --p-muted-2: #7a8594; --p-faint: #8e98a6;
    --accent-base: #2d5a8c;
  }
  [data-palette="slate"][data-theme="dark"] {
    --rail: #16202b; --rail-fg: #aeb8c3; --rail-link: #a8c4e0; --hair: #3f6d95;
    --bg: #11161c; --panel: #1b222a; --line: #313b47; --line-strong: #414c59;
    --tint: #171d24; --tint-soft: #1f262f; --draft-hover: #1c2634;
    --p-ink: #edf1f5; --p-ink-2: #d5dce4; --p-ink-soft: #bfc8d2;
    --p-muted: #9ba6b2; --p-muted-2: #8a95a1; --p-faint: #737e8a;
    --accent-base: #2d5a8c;
  }

  /* Plain greys, a steel-blue accent. */
  [data-palette="graphite"][data-theme="light"] {
    --rail: #202023; --rail-fg: #c6c6cb; --rail-link: #c3cedb; --hair: #5f6b7a;
    --bg: #f4f4f5; --panel: #ffffff; --line: #dcdce0; --line-strong: #cbcbd0;
    --tint: #f2f2f4; --tint-soft: #f9f9fa; --draft-hover: #e9e9ed;
    --p-ink: #1c1c1f; --p-ink-2: #3a3a3f; --p-ink-soft: #4e4e54;
    --p-muted: #616168; --p-muted-2: #7e7e85; --p-faint: #919198;
    --accent-base: #34557a;
  }
  [data-palette="graphite"][data-theme="dark"] {
    --rail: #16161a; --rail-fg: #b3b3b9; --rail-link: #b6c4d6; --hair: #53606f;
    --bg: #141416; --panel: #1f1f22; --line: #36363b; --line-strong: #46464c;
    --tint: #19191c; --tint-soft: #232327; --draft-hover: #1f2026;
    --p-ink: #f0f0f2; --p-ink-2: #d9d9dd; --p-ink-soft: #c3c3c8;
    --p-muted: #a0a0a7; --p-muted-2: #8e8e95; --p-faint: #77777e;
    --accent-base: #34557a;
  }

  /* For anybody who finds grey text hard to read: black on white, lines that
     show. The one theme where --bg and --panel are the same, so a panel is
     told by its line rather than by its ground — which is why the lines are
     the darkest of any theme here. */
  [data-palette="contrast"][data-theme="light"] {
    --rail: #000000; --rail-fg: #ffffff; --rail-link: #ffffff; --hair: #ffffff;
    --bg: #ffffff; --panel: #ffffff; --line: #a8a8a8; --line-strong: #7a7a7a;
    --tint: #efefef; --tint-soft: #f7f7f7; --draft-hover: #e4e4e4;
    --p-ink: #000000; --p-ink-2: #111111; --p-ink-soft: #222222;
    --p-muted: #333333; --p-muted-2: #444444; --p-faint: #555555;
    --accent-base: #004f9e;
  }
  [data-palette="contrast"][data-theme="dark"] {
    --rail: #000000; --rail-fg: #ffffff; --rail-link: #ffffff; --hair: #ffffff;
    --bg: #000000; --panel: #0d0d0d; --line: #5a5a5a; --line-strong: #8a8a8a;
    --tint: #141414; --tint-soft: #1c1c1c; --draft-hover: #1a1a1a;
    --p-ink: #ffffff; --p-ink-2: #f2f2f2; --p-ink-soft: #e6e6e6;
    --p-muted: #d0d0d0; --p-muted-2: #c0c0c0; --p-faint: #a8a8a8;
    --accent-base: #1f5fa8;
  }

  /* An accent chosen apart from the theme's own: ONE colour, dark enough to
     carry white text, because that is what --accent is for. No green and no
     red — `services/appearance.ts` says why, and it is this repository's own
     rule rather than a shortage of colours. */
  [data-accent="teal"][data-theme] { --accent-base: var(--ac-teal); }
  [data-accent="blue"][data-theme] { --accent-base: var(--ac-blue); }
  [data-accent="indigo"][data-theme] { --accent-base: var(--ac-indigo); }
  [data-accent="ochre"][data-theme] { --accent-base: var(--ac-ochre); }
  [data-accent="plum"][data-theme] { --accent-base: var(--ac-plum); }
  [data-accent="graphite"][data-theme] { --accent-base: var(--ac-graphite); }

  /*
   * The accent's whole family, from that one colour.
   *
   * Seven names, because this application splits a FILL from TEXT (`--accent`
   * carries white text and `--accent-text` is read on the panel) and keeps two
   * paler golds for the quiet edges. The standard look's seven are hand-mixed
   * in `:root` and are NOT these: a chosen accent is one colour with a ramp
   * derived from it, which is honest about what it is. That is also why these
   * rules need an attribute — without one they would re-derive the firm's own
   * family and shift it by a few digits for no reason.
   */
  [data-palette][data-theme="light"], [data-accent][data-theme="light"] {
    --accent: var(--accent-base);
    --accent-dark: color-mix(in srgb, var(--accent-base) 80%, black);
    --accent-text: var(--accent-base);
    --accent-text-strong: color-mix(in srgb, var(--accent-base) 80%, black);
    --accent-tint: color-mix(in srgb, var(--accent-base) 10%, white);
    --accent-soft: color-mix(in srgb, var(--accent-base) 55%, white);
    --accent-soft-dark: color-mix(in srgb, var(--accent-base) 68%, white);
  }
  /* At night the FILL stays dark enough for white text and the TEXT is lifted
     off the panel — the same split the standard dark theme makes by hand. */
  [data-palette][data-theme="dark"], [data-accent][data-theme="dark"] {
    --accent: color-mix(in srgb, var(--accent-base) 88%, white);
    --accent-dark: color-mix(in srgb, var(--accent-base) 80%, black);
    --accent-text: color-mix(in srgb, var(--accent-base) 45%, white);
    --accent-text-strong: color-mix(in srgb, var(--accent-base) 30%, white);
    --accent-tint: color-mix(in srgb, var(--accent-base) 22%, var(--panel));
    --accent-soft: color-mix(in srgb, var(--accent-base) 55%, white);
    --accent-soft-dark: color-mix(in srgb, var(--accent-base) 68%, white);
  }

  /*
   * The text a step along the theme's own scale, and the two directions are
   * NOT symmetrical — which is a property of the scale rather than an
   * oversight. `--ink` is already the strongest step, so "stronger" has
   * nowhere to take it and moves the muted text instead; "softer" moves both.
   * Softer never goes past the theme's own `--p-muted`, so a label stays
   * readable rather than becoming decoration.
   */
  :root[data-text="stronger"] {
    --ink-soft: var(--p-ink-2);
  }
  :root[data-text="softer"] {
    --ink: var(--p-ink-2);
    --ink-soft: color-mix(in srgb, var(--p-ink-soft) 50%, var(--p-muted));
  }
}

* { box-sizing: border-box; }

/**
 * The `hidden` attribute must actually hide things.
 *
 * A browser's own rule for [hidden] is `display: none`, but it loses to any
 * class rule that sets `display` — so `.signin { display: grid }` left the
 * sign-in overlay laid out at `position: fixed; inset: 0` while hidden,
 * invisible and swallowing every click in the application behind it. Nothing
 * looked wrong; buttons simply did nothing.
 *
 * One rule, applied to everything, so the next element with a display type
 * cannot reintroduce it.
 */
[hidden] { display: none !important; }

/**
 * Every control in the page's own face. (ROBOTO-1)
 *
 * A browser sets buttons, boxes and lists in its OWN default font unless told
 * otherwise, and only the styled buttons here were told — so the passkey
 * button on the sign-in card, and every plain button and box, stayed in Arial
 * once the page went to Roboto. The family only: sizes are each control's own.
 */
button, input, select, textarea, optgroup { font-family: inherit; }

body {
  margin: 0;
  font: 14.5px/1.55 var(--sans);
  color: var(--ink);
  background: var(--bg);
  min-height: 100vh;
  /**
   * Every digit the same width, everywhere, not only in the columns that ask
   * for it. This is a payroll: a figure in a sentence, a figure in a badge and
   * a figure in a table are all the same figure, and a proportional 1 makes
   * two of them disagree about where the decimal point is.
   */
  font-variant-numeric: tabular-nums;
}

/**
 * ── The application wrapper, and why it is a box after all ──────────────
 *
 * The bar and `main` are wrapped in `#app` so the whole application can be
 * hidden behind the sign-in screen. It was `display: contents`, which removes
 * the wrapper from the layout without removing it from the document — the
 * right answer while nothing needed to know how wide it was.
 *
 * Something does now. The text-size control scales the screens with `zoom`,
 * and `zoom` does not move a media query: `matchMedia` gives the same answer
 * at 150% as at 100%, so a 1024px window at 150% — which has the room of a
 * 683px one — kept the desktop rules and the page slid sideways. The control
 * exists for somebody who cannot read the screen at 100%, and it handed that
 * person a page they had to drag left and right. (A2)
 *
 * A CONTAINER query does follow the zoom, because a container is measured in
 * its own coordinate space and the zoom is part of that space. So every
 * narrow-width rule below asks this element how much room there is rather than
 * asking the window, and the answer is right at every size. An element with no
 * box cannot be asked, which is why this is a block now: `[hidden]` above is
 * `!important` and still wins, and `body` is a plain block, so nothing else
 * about the layout changes.
 *
 * A browser without container queries would apply none of those rules and draw
 * the desktop layout on a phone. It would also be a browser without `:has()`,
 * which this file has used since the dark palette — container queries are the
 * older of the two — so there is nothing here to guard against that could not
 * already be said about the rest of the stylesheet.
 */
#app {
  display: block;
  container-type: inline-size;
  container-name: app;
}

/**
 * ── The frame: top bar, menu down the left, footer ──────────── (LAYOUT-1) ──
 *
 * Reckoner's console shell, measured off its build (Anvil's ConsoleShell) and
 * written again here: a 240px menu that folds to 68px, a top bar across both
 * columns, the screen, and a footer under the screen only. Below 860px the
 * menu leaves the grid and becomes a panel that slides in from the left;
 * below 640px the name in the top bar gives way to its initials.
 *
 * One difference, on purpose: Reckoner scrolls the screen INSIDE the frame;
 * here the page itself scrolls and the bar and the menu stay put (`sticky`).
 * Printing, full-page pictures and the text-size zoom all assume the page is
 * what scrolls, and nobody can see the difference.
 *
 * Every breakpoint is a CONTAINER query on `#app`, for the reason given above:
 * the zoom moves a container query and does not move a media query. That is
 * also why the grid is `.shell` inside `#app` rather than `#app` itself — an
 * element cannot ask its own size.
 */
.shell {
  --top-h: 56px;
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  grid-template-rows: var(--top-h) minmax(0, 1fr) auto;
  min-height: 100vh;
  background: var(--bg);
}
.shell.shell--folded { grid-template-columns: 68px minmax(0, 1fr); }

/* The top bar, across both columns, staying put while the page scrolls. */
.shell-top {
  grid-column: 1 / -1;
  grid-row: 1;
  position: sticky;
  top: 0;
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  padding: 0 20px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  font-size: 13px;
}

/**
 * The monogram: the firm's letters in a small rounded square, as Reckoner
 * shows its own mark. Text, not a picture, so it is still just "KP".
 */
.shell-top .mark {
  display: inline-grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 8px;
  background: var(--rail);
  color: var(--rail-fg-on);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
}
.shell-wordmark { font-size: 15px; font-weight: 600; color: var(--ink); white-space: nowrap; flex: none; }

/* The company sits on the right, beside the size, light/dark and the person,
   by the owner's choice: the left of the bar is the mark and nothing else. */
.shell-company { display: flex; align-items: center; gap: 12px; min-width: 0; margin-left: auto; padding-right: 6px; }
.shell-end { display: flex; align-items: center; gap: 10px; flex: none; }

.shell-hamburger {
  display: none;
  align-items: center; justify-content: center;
  width: 34px; height: 34px; flex: none;
  border: 0; border-radius: 8px; background: transparent; color: var(--ink);
  cursor: pointer; padding: 0;
}
.shell-hamburger:hover { background: var(--tint); }
.shell-hamburger .ico { width: 20px; height: 20px; }

/* The menu down the left. */
.shell-side {
  grid-column: 1;
  grid-row: 2 / 4;
  position: sticky;
  top: var(--top-h);
  align-self: start;
  height: calc(100vh - var(--top-h));
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 14px;
  overflow-y: auto;
  background: var(--panel);
  border-right: 1px solid var(--line);
  z-index: 4;
}
.side-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0 4px 4px; }
.side-label { font-size: 11.5px; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-soft); }
.side-collapse {
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: transparent; cursor: pointer; padding: 4px;
  border-radius: 8px; color: var(--ink-soft);
}
.side-collapse:hover { background: var(--tint); color: var(--ink); }
.side-collapse .ico { width: 18px; height: 18px; display: block; }
.side-collapse .when-folded { display: none; }
.shell--folded .side-collapse .when-open { display: none; }
.shell--folded .side-collapse .when-folded { display: inline-flex; }

.shell-side .modules { display: flex; flex-direction: column; gap: 2px; }
.side-foot { margin-top: auto; display: flex; flex-direction: column; gap: 2px; padding-top: 8px; border-top: 1px solid var(--line); }

.shell-side button.rail-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  font-size: 14px;
  padding: 8px 12px;
  cursor: pointer;
  white-space: nowrap;
}
.shell-side button.rail-btn .ico { width: 18px; height: 18px; flex: none; }
.shell-side button.rail-btn:hover:not(:disabled) { background: var(--tint); color: var(--ink); }
.shell-side button.rail-btn[aria-current="true"] {
  background: var(--accent-tint);
  color: var(--accent-text);
  font-weight: 600;
}
.shell-side button.rail-btn:disabled { opacity: .45; cursor: default; }
.shell-side button.rail-btn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: -2px; }

/* Folded: icons only, each still named by its title and its hidden label. */
.shell--folded .shell-side { padding-left: 10px; padding-right: 10px; }
.shell--folded .side-label { display: none; }
.shell--folded .side-head { justify-content: center; padding: 0 0 4px; }
.shell--folded .shell-side button.rail-btn { justify-content: center; padding: 9px 6px; }
.shell--folded .shell-side .navlabel {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.shell > main { grid-column: 2; grid-row: 2; min-width: 0; }

/* The footer, under the screen and never under the menu. */
.shell-foot {
  grid-column: 2;
  grid-row: 3;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 28px;
  background: var(--panel);
  border-top: 1px solid var(--line);
  color: var(--ink-soft);
  font-size: 12.5px;
}
.shell-foot .foot-name { color: var(--ink); font-weight: 500; }
.shell-foot .foot-build:not(:empty)::before { content: "·"; margin-right: 10px; color: var(--line-strong); }

.shell-scrim { display: none; }

/* ── The person's menu, top right ── */
.account { position: relative; flex: none; }
.account-btn {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid transparent; border-radius: 8px;
  background: transparent; color: var(--ink);
  padding: 4px 6px 4px 4px; cursor: pointer; font: inherit; max-width: 260px;
}
.account-btn:hover, .account-btn[aria-expanded="true"] { background: var(--tint); }
.account-btn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 1px; }
.account-btn > .ico { width: 16px; height: 16px; flex: none; color: var(--ink-soft); }
.avatar {
  display: inline-grid; place-items: center;
  width: 30px; height: 30px; flex: none; border-radius: 50%;
  background: var(--accent-tint); color: var(--accent-text);
  font-size: 12px; font-weight: 600;
}
/**
 * The name is the one thing on the bar with no width of its own — it is
 * whatever the record says (A1). Capped and trimmed rather than hidden: the
 * first name and the start of the second is enough to see whose session this
 * is, and the menu it opens has it in full.
 */
.account-id { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; line-height: 1.2; }
.account-name, .account-role {
  max-width: 170px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.account-name { font-size: 13px; font-weight: 500; }
.account-role { font-size: 11.5px; color: var(--ink-soft); }

.account-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 20;
  min-width: 230px;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 10px 15px -3px rgba(0,0,0,.1), 0 4px 6px -4px rgba(0,0,0,.1);
}
.account-menu .menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  border: 0; border-radius: 6px; background: transparent; color: var(--ink);
  font: inherit; font-size: 13.5px; text-align: left; padding: 8px 10px; cursor: pointer;
}
.account-menu .menu-item .ico { width: 16px; height: 16px; flex: none; color: var(--ink-soft); }
.account-menu .menu-item:hover, .account-menu .menu-item:focus-visible { background: var(--tint); outline: none; }
.account-menu .menu-rule { border: 0; border-top: 1px solid var(--line); margin: 6px 4px; }

/* The finish-with-this-client link beside the company. */
.shell-company button.link { padding: 4px 2px; white-space: nowrap; }

/* ── Narrower: the menu becomes a panel that slides in ── */
@container app (max-width: 860px) {
  .shell, .shell.shell--folded { grid-template-columns: minmax(0, 1fr); }
  .shell > main, .shell-foot { grid-column: 1; }
  .shell-top { padding: 0 12px; gap: 10px; }
  .shell-hamburger { display: inline-flex; }
  .side-collapse { display: none; }
  .shell-side {
    position: fixed; top: 0; left: 0; bottom: 0; height: 100vh; height: 100dvh;
    width: 260px; z-index: 50;
    transform: translateX(-100%);
    transition: transform 200ms cubic-bezier(0, 0, 0.2, 1);
    box-shadow: 0 25px 50px -12px rgba(0,0,0,.25);
    visibility: hidden;
  }
  .shell-side.is-open { transform: translateX(0); visibility: visible; }
  /* A folded menu is a desktop choice; the slide-in panel always has names. */
  .shell--folded .shell-side .navlabel {
    position: static; width: auto; height: auto; margin: 0;
    overflow: visible; clip: auto; clip-path: none;
  }
  .shell--folded .shell-side button.rail-btn { justify-content: flex-start; padding: 8px 12px; }
  .shell--folded .side-label { display: block; }
  .shell--folded .side-head { justify-content: space-between; }
  .shell-scrim {
    display: block; position: fixed; inset: 0; z-index: 45;
    background: rgb(0 0 0 / .35);
  }
  .shell-scrim[hidden] { display: none !important; }
  .shell-foot { padding: 10px 16px; }
}

@container app (max-width: 640px) {
  .shell-wordmark { display: none; }
  .account-id { display: none; }
  .account-btn > .ico { display: none; }
  .account-btn { padding: 3px; }
  .entity-switch > span:first-child { display: none; }
  .entity-switch select { max-width: 150px; }
  .shell-end { gap: 6px; }
}

@container app (max-width: 420px) {
  .shell-top .mark { display: none; }
  .text-size .aa { display: none; }
  .entity-switch select { max-width: 120px; }
}

main { padding: 22px 28px 60px; min-width: 0; }

header.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 12px;
  margin-bottom: 22px;
}

/**
 * ── Which words are set in the serif ──────────────────────────────────────
 *
 * The named things: the screen, the panel, the window, the card. One list,
 * here, so a heading added later is set like the others without anybody
 * having to remember a rule.
 *
 * A small heading that is really a LABEL — `.window-body h3`, the grey
 * caption over a group of fields, and `legend` — is deliberately not in it.
 * Those are interface furniture and belong with the boxes underneath them,
 * not with the titles.
 */
header.top h1,
.panel h2,
.panel h3,
.window-body h2,
.signin-card h1,
.signin-card h2,
.note-head h3 {
  font-family: var(--sans);
  /* A serif at the same nominal size reads a shade smaller; the headings are
     nudged up rather than left looking like the body text got bolder. */
  letter-spacing: .002em;
}

header.top h1 { font-size: 19px; margin: 0; font-weight: 600; }
.muted { color: var(--ink-soft); }

.entity-switch { display: flex; align-items: center; gap: 8px; min-width: 0; color: var(--ink-soft); }
.entity-switch select {
  border: 1px solid var(--line);
  border-radius: 8px;
  height: 34px;
  padding: 0 10px;
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  max-width: 260px;
  min-width: 0;
}
.entity-switch select:hover { border-color: var(--accent-soft); }
/* "No company selected" gives way before the controls beside it do. */
#entity-none { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.text-size select { flex: none; min-width: 64px; }
.entity-switch select:focus-visible { outline: none; border-color: var(--accent-text); box-shadow: 0 0 0 3px var(--accent-tint); }
/* The options render on the operating system's own menu, which is light. */
.entity-switch select option { color: var(--ink); background: var(--panel); }

/**
 * ── The text size, and the one rule that keeps it off the paper ───────────
 *
 * `zoom` rather than a root font-size, because every size in this file is in
 * PIXELS: moving the root would leave every table, panel and field box exactly
 * as cramped as before, and somebody asking for bigger screens means the screen.
 * The steps are a list in services/ui-zoom.ts — `zoom` cannot be computed from
 * an attribute, so each one needs its own rule here, and `ui-zoom.test.ts` fails
 * if this block and that list disagree.
 *
 * INSIDE `@media screen`, and that is not tidiness. A payslip is a document
 * somebody is handed and a certificate is filed; at 150% on paper a quarter of
 * the page would be off the sheet, and nobody printing it would connect that to
 * a text size they set weeks ago on a different screen. The `@media print`
 * block at the end of this file does not undo it and does not need to: a rule
 * inside `@media screen` never applies to paper in the first place, which is
 * why the zoom is written here rather than cancelled there.
 *
 * 100% has no rule. At 100% the attribute is removed rather than set, so the
 * ordinary case carries no `zoom` at all and cannot be affected by a mistake in
 * one of these.
 */
@media screen {
  html[data-zoom="90"] { zoom: 0.9; }
  html[data-zoom="110"] { zoom: 1.1; }
  html[data-zoom="125"] { zoom: 1.25; }
  html[data-zoom="150"] { zoom: 1.5; }
}

/**
 * The control itself: an "Aa" beside a small select, on the bar.
 *
 * "Aa" rather than a magnifier, because the magnifier on a top bar means search
 * everywhere else somebody has used a computer. The letters are not the label —
 * they are `aria-hidden` and the select carries the real one — so nothing here is
 * told by a glyph alone.
 */
.text-size { display: inline-flex; align-items: center; gap: 5px; color: var(--ink-soft); cursor: pointer; }
.text-size .aa { font-family: var(--sans); font-weight: 600; font-size: 13px; letter-spacing: -0.02em; }
/* The same box as the company switcher beside it, one size down: it is the
   quieter of the two and should not read as the more important. */
.text-size select {
  border: 1px solid var(--line);
  border-radius: 8px;
  height: 30px;
  padding: 0 4px;
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
}
.text-size select option { color: var(--ink); background: var(--panel); }

/**
 * Light, dark, or the machine's — one button of three states. (THEME-1)
 *
 * On the bar beside the size control, drawn the way the rail's own buttons are
 * drawn: transparent, the rail's quiet text colour, lit on hover. The label is
 * carried for a screen reader and hidden from the eye, because the icon says it
 * to anybody who can see the icon.
 */
.shell-top button.theme-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  width: 34px; height: 34px; border: 0; border-radius: 8px;
  background: transparent; color: var(--ink-soft); cursor: pointer; padding: 0;
}
.shell-top button.theme-btn svg { width: 18px; height: 18px; flex: none; }
.shell-top button.theme-btn:hover { background: var(--tint); color: var(--ink); }
.shell-top button.theme-btn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 1px; }

/**
 * Said to a screen reader and not to the eye.
 *
 * Not `display: none` and not `visibility: hidden`, either of which takes it
 * out of the accessibility tree as well and leaves an unlabelled button.
 */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/**
 * ── Skip to the screen ────────────────────────────────────────────────────
 *
 * The other half of the rule above: said to nobody until it is wanted, and
 * then said plainly. Off the top of the page rather than in `.visually-hidden`,
 * because this one has to be READABLE the moment it takes focus and a clipped
 * 1px box cannot grow back into a legible link.
 *
 * It is drawn as a panel rather than as part of the bar on purpose. A control
 * that appears in the middle of the chrome and disappears again reads as a
 * rendering fault; one that drops in over the top-left corner reads as what it
 * is — something the keyboard just summoned.
 *
 * Hidden while the sign-in card is up. `#app` carries `[hidden]` until there is
 * a session, its target is inside it, and a skip link that lands nowhere is
 * worse than none. `:has()` because the link sits BEFORE `#app` in the markup
 * and no sibling combinator reaches backwards; this file has used `:has()`
 * since the dark palette. (SKIP-1)
 */
.skip-link {
  position: absolute;
  top: -100px;
  left: 12px;
  z-index: 100;
  padding: 10px 16px;
  background: var(--panel);
  color: var(--accent-text);
  border: 1px solid var(--accent-soft);
  border-radius: 6px;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus {
  top: 12px;
  outline: 2px solid var(--accent-text);
  outline-offset: 1px;
}
body:has(#app[hidden]) .skip-link { display: none; }

/* And the target takes focus without being outlined like a text box: the page
   has visibly moved, which is the feedback, and a ring around the whole screen
   is not. */
#screen:focus { outline: none; }

/* ── Panels ── */
.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 20px 22px;
  max-width: 640px;
}
.panel.wide { max-width: 940px; }

/* The "or" between two ways of doing the same thing — a rule with the word
   sitting in the gap, so neither route reads as the afterthought. */
.or {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 18px 0 12px;
  color: var(--ink-soft);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.or::before,
.or::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.panel h2 { font-size: 16px; margin: 0 0 10px; font-weight: 600; }
.panel h3 { font-size: 14px; margin: 22px 0 8px; font-weight: 600; }
.panel > p { margin: 0 0 16px; color: var(--ink-soft); max-width: 60ch; }

.panel-head { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.panel-head h2 { margin: 0; }
/* More than one button beside a heading — print it, then leave it. */
.head-actions { display: flex; gap: 8px; align-items: center; }

/**
 * ── A tab bar inside a panel ──
 *
 * The same job the `window-sections` box does on the employee screen, laid out
 * across the top of a form: a long form split into named parts, so somebody
 * adding an employee is not scrolling past twenty fields to reach the bank
 * details. The underline carries the selection rather than a filled block,
 * which keeps it quiet enough to sit directly under a heading.
 */
.tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--line);
  margin: 0 0 18px;
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tabs .tab {
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 13px;
  color: var(--ink-soft);
  padding: 9px 14px;
  cursor: pointer;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.tabs .tab:hover { color: var(--ink); background: var(--tint); }
.tabs .tab[aria-selected="true"] {
  color: var(--accent-text);
  border-bottom-color: var(--accent-text);
  font-weight: 600;
}

/* An employee's record: fourteen parts, so the tabs WRAP onto a second line
   rather than scroll sideways out of sight. A hidden scrollbar on a row this
   long would hide the parts a person came for. (FORM-1) */
.tabs.record-tabs { flex-wrap: wrap; overflow: visible; row-gap: 0; }
.tabs.record-tabs .tab { padding: 8px 12px; }
/* On Add an employee, the parts that open once the person is saved. */
.tabs.record-tabs .tab.later { color: var(--ink-faint, var(--ink-soft)); font-style: italic; }

/* Panes carry the fieldset's old spacing now that the legend has become a tab. */
.tab-pane { padding-top: 2px; }

/* ── Forms ── */
fieldset { border: 0; border-top: 1px solid var(--line); margin: 0 0 4px; padding: 14px 0 6px; }
fieldset:first-of-type { border-top: 0; padding-top: 0; }
legend { font-weight: 600; font-size: 13px; color: var(--ink-soft); padding: 0 8px 0 0; }

.row { display: flex; flex-wrap: wrap; gap: 16px; }
.row .field { flex: 1 1 200px; }

.field { margin-bottom: 14px; }
.field label { display: block; font-size: 13px; margin-bottom: 4px; font-weight: 500; }
/* A group of tick boxes under one label — a company's pay frequencies. (PAY-2)
   The group's name is a span styled as a label: a <label> names one control. */
.field > .label { display: block; font-size: 13px; margin-bottom: 4px; font-weight: 500; }
.field .checks { display: flex; flex-wrap: wrap; gap: 6px 18px; padding: 6px 0; }
.field .checks label { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-weight: 400; }
.field .checks input { width: auto; }
.field .req { font-style: normal; font-size: 11px; color: var(--ink-soft); font-weight: 400; }
.field input, .field select, .field textarea {
  width: 100%;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  padding: 7px 9px;
  font: inherit;
  background: var(--panel);
  color: var(--ink);
}
.field input:focus, .field select:focus, .field textarea:focus { outline: 2px solid var(--accent-text); outline-offset: -1px; border-color: var(--accent-text); }
.field small { display: block; margin-top: 4px; font-size: 12px; color: var(--ink-soft); }
.field.has-error input, .field.has-error select { border-color: var(--bad); }

/*
 * ── The Appearance panel's choices ──────────────────────────── (LOOK-1) ──
 *
 * Three groups of buttons, each a radio. The CHOSEN one is told by its border,
 * its ground AND a word — the name is always written out, so nothing here is
 * told by colour alone, which is the same rule every status on every screen
 * follows.
 */
.ap-choices { display: flex; flex-wrap: wrap; gap: 10px; margin: 10px 0 18px; }
.ap-accents { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 18px; }
.ap-choice {
  display: flex; flex-direction: column; gap: 3px; align-items: flex-start;
  border: 1px solid var(--line-strong); border-radius: 6px;
  background: var(--panel); color: var(--ink);
  padding: 9px 13px; font: inherit; text-align: left; cursor: pointer;
  max-width: 260px;
}
.ap-accents .ap-choice { flex-direction: row; align-items: center; gap: 7px; }
.ap-choice:hover:not(.on) { background: var(--tint); }
.ap-choice.on { border-color: var(--accent); background: var(--accent-tint); }
.ap-choice.on .ap-name { font-weight: 600; }
.ap-name { font-size: 13px; }
.ap-note { font-size: 12px; color: var(--ink-soft); }
/*
 * The swatch, drawn from the value rather than from a colour written in
 * `app.js` — which holds no hex at all, and `theme.test.ts` asserts that. The
 * ring is the panel's own line so a dark swatch on a dark panel still has an
 * edge.
 */
.ap-dot {
  width: 15px; height: 15px; border-radius: 50%;
  border: 1px solid var(--line-strong); flex: none;
}
.ap-dot[data-accent="theme"] { background: var(--accent); }
.ap-dot[data-accent="teal"] { background: var(--ac-teal); }
.ap-dot[data-accent="blue"] { background: var(--ac-blue); }
.ap-dot[data-accent="indigo"] { background: var(--ac-indigo); }
.ap-dot[data-accent="ochre"] { background: var(--ac-ochre); }
.ap-dot[data-accent="plum"] { background: var(--ac-plum); }
.ap-dot[data-accent="graphite"] { background: var(--ac-graphite); }

.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
button.primary, .actions button, .inline-form button {
  border-radius: 6px;
  padding: 8px 16px;
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--line-strong);
  background: var(--panel);
  color: var(--ink);
}
button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
button.primary:hover:not(:disabled) { background: var(--accent-dark); border-color: var(--accent-dark); }
/* The one button that throws an installation's contents away. */
button.danger { background: var(--bad); border-color: var(--bad); color: #fff; }
#restore-block h3 { margin: 18px 0 8px; font-size: 1rem; }

/**
 * A disabled button has to LOOK disabled.
 *
 * Only the rail had a disabled style, so everywhere else a button that did
 * nothing was indistinguishable from one that worked — the "add a passkey"
 * button sat there in full accent blue on a server where passkeys are switched
 * off, and clicking it was the only way to find out. The explanation next to it
 * is not much use if the control it explains looks live.
 */
button:disabled,
button.primary:disabled {
  opacity: .45;
  cursor: not-allowed;
}

/* ── Notices ── */
.notice { border-radius: 6px; padding: 10px 14px; margin-bottom: 16px; font-size: 13px; }
.notice ul { margin: 6px 0 0; padding-left: 18px; }
.notice.bad { background: var(--bad-bg); color: var(--bad); border: 1px solid var(--bad-line); }
.notice.warn { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn-line); }
.notice.good { background: var(--good-bg); color: var(--good); border: 1px solid var(--good-line); }

/* ── The server could not be reached ──────────────────────────── (NET-1)
 *
 * Pinned to the top of the window rather than placed in a screen, because it
 * is true of the page and not of whatever is being looked at — and because the
 * screen it would otherwise sit in may be the one that failed to draw.
 *
 * It takes `.notice.bad`'s colours from the variables above and mixes none of
 * its own, so it answers in both themes without a second rule. No `@media`
 * width query: the house rule is that narrow-width rules ask the container,
 * and this is not in one — it is bounded by `max-width` and centred instead,
 * which needs no breakpoint at all.
 *
 * `margin-bottom: 0` overrides `.notice`'s 16px: fixed to the viewport, that
 * margin is 16px of nothing under a floating box.
 */
#connection-lost {
  position: fixed;
  z-index: 60;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  width: calc(100% - 32px);
  max-width: 560px;
  margin-bottom: 0;
  box-shadow: 0 2px 10px rgb(0 0 0 / 18%);
}
#connection-lost strong { display: block; }


/* ── Tables ── */
table.grid { width: 100%; border-collapse: collapse; font-size: 13px; }
table.grid th {
  text-align: left;
  font-weight: 600;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--line-strong);
  padding: 8px 10px;
}
table.grid td { border-bottom: 1px solid var(--line); padding: 9px 10px; }
table.grid tbody tr { cursor: pointer; }
table.grid tbody tr:hover { background: var(--tint); }
/*
 * A row that opens nothing does not invite a click. The payroll register is
 * clickable because each row opens a payslip; for a viewer the payslip is
 * refused, so the row is inert and says so with its cursor. (SENS-1)
 */
table.grid tbody tr.unclickable { cursor: default; }
table.grid tbody tr.unclickable:hover { background: transparent; }
/**
 * ── Reviewing a run ──
 *
 * The exception list. Several reasons can apply to one person — somebody who
 * left AND had their bank details changed is two facts — so the reasons are a
 * list inside the cell rather than a sentence, and the cell has to stop looking
 * like a bulleted paragraph.
 */
#review-panel { margin: 22px 0; }
#review-panel .panel-head { margin-bottom: 6px; }
#review-table ul.reasons { margin: 0; padding-left: 16px; }
#review-table ul.reasons li { margin: 1px 0; }
/* A moved bank account shows up in no figure on this page — the register is
   identical and the money goes somewhere else. It does not get to look like
   the others. */
#review-table .reason-loud { color: var(--bad); font-weight: 500; }
#review-table tbody tr { cursor: default; }
#review-table tbody tr:hover { background: transparent; }
.reviewed { color: var(--good); font-size: 12px; white-space: nowrap; }

/**
 * ── One-off entries on a run ──
 *
 * The panel sits between the actions and the register, and the two tables are
 * separated deliberately: without the rule below, the entries table's last row
 * and the register's header sat flush against each other and the whole thing
 * read as ONE table with a second set of column headings in the middle of it.
 */
#entries-panel { margin: 22px 0; }
#entries-panel .panel-head { margin-bottom: 6px; }
#entries-panel + .table-scroll > table.grid, #entries-panel + table.grid { margin-top: 24px; border-top: 1px solid var(--line-strong); }
#entries-form { margin: 14px 0 18px; }
#entries-form .muted { font-size: 12px; margin: 4px 0 0; }
/* These rows are not clickable, so they do not offer to be: `table.grid`
   assumes a row opens something, and every other grid in the application
   does. */
/**
 * A checkbox is not a text box, and `.field input { width: 100% }` treats it
 * like one — a stretched checkbox renders centred in its own box, floating
 * above its label with nothing beside it. This puts the box and its words on
 * one line, where a checkbox belongs.
 */
.field.check label { display: flex; align-items: center; gap: 8px; font-weight: 400; }
.field.check input[type="checkbox"] { width: auto; margin: 0; }

#entries-table tbody tr { cursor: default; }
#entries-table tbody tr:hover { background: transparent; }

/* An unfinished record should not read as a finished one at a glance. */
table.grid tbody tr.is-draft { background: var(--warn-bg); }
table.grid tbody tr.is-draft:hover { background: var(--draft-hover); }

/* Somebody who has left. Red, and only red — no strike-through, because the
   record is still to be read: they were paid this year and they are on the
   TD7. The colour says "not on the next run", not "ignore this line". */
table.grid tbody tr.is-terminated td { color: var(--bad); }

/* ── Detail lists ── */
dl.kv { display: grid; grid-template-columns: 220px 1fr; gap: 7px 18px; margin: 14px 0 0; font-size: 13px; }
dl.kv dt { color: var(--ink-soft); }
dl.kv dd { margin: 0; }

ul.checklist { margin: 0; padding-left: 18px; font-size: 13px; color: var(--warn); }
ul.checklist li { margin-bottom: 3px; }

/* The old top bar's own breakpoints (A1, B3, THEME-1) went with it: the
   modules are in the menu down the left now, so the bar holds the company,
   two small controls and the person, and its breakpoints are with the frame
   above (LAYOUT-1). `shot-topbar-fit.mjs` still sweeps 320 to 1440px with a
   long name and a long company and fails on a pixel of sideways scroll. */

@container app (max-width: 720px) {
  main { padding: 16px; }
  header.top { flex-wrap: wrap; }
  dl.kv { grid-template-columns: 1fr; gap: 2px 0; }
  dl.kv dd { margin-bottom: 8px; }
}

/* ── The single-employee window: one box names the part you are in ── */
.window {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  max-width: 1040px;
}

/**
 * Settings is the same window, wider.
 *
 * Six parts rather than thirteen, and two of them — the linked-application keys
 * and the year's rates — are tables the employee screen has no equivalent of.
 * Only the room for the content changes, so the two screens still read as the
 * same furniture. (SET-1)
 */
.window.wide { max-width: 1240px; }

/*
 * ── The part, as a dropdown ─────────────────────────────────────── (DROP-1)
 *
 * This was a 200px column of buttons down the left of both windows, with the
 * open one carrying a bronze edge. It is one box above the content now, by the
 * owner's instruction — and the column going is what gives the record and the
 * settings form the whole width back. Thirteen sections down the left is a
 * list read top to bottom before every move; a box naming the part you are in
 * says the same thing in one line.
 *
 * `.window` is no longer a grid, which is the whole of the layout change: one
 * column, with this sitting at the top of the body.
 *
 * The TOMBSTONE matters as much as the rule. `.window-nav` was the shared
 * furniture of two screens and it is gone — a rule left behind here would be
 * styling nothing while reading as the thing in charge of this screen, and
 * `settings-sections.test.ts` asserts it has not come back.
 */
.window-sections {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 0 16px;
  margin: 0 0 18px;
  border-bottom: 1px solid var(--line);
}
.window-sections > span {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  flex: none;
}
.window-sections select {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent-text);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  padding: 7px 10px;
  cursor: pointer;
  /* Capped, like everything on the top bar: "Additional contributions" is a
     long option and an uncapped box would set the width of the window. (A1) */
  max-width: 320px;
}
.window-sections select:hover { border-color: var(--accent-soft); }

.window-body { padding: 20px 24px; }
.window-body h2 { font-size: 16px; margin: 0 0 14px; font-weight: 600; }
.window-body h3 { font-size: 13px; margin: 20px 0 8px; font-weight: 600; color: var(--ink-soft); }

.inline-form { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin-top: 14px; }
.inline-form .field { margin: 0; min-width: 140px; flex: 0 1 auto; }
.inline-form button { align-self: flex-end; }

.pill {
  display: inline-block;
  font-size: 11px;
  border-radius: 10px;
  padding: 1px 8px;
  background: var(--accent-tint);
  color: var(--ink-soft);
  margin-left: 6px;
}
.pill.a2 { background: var(--warn-bg); color: var(--warn); }
.mono { font-variant-numeric: tabular-nums; }
/**
 * Scoped to `table.grid`, and deliberately.
 *
 * A bare `th.num` loses on specificity to `table.grid th { text-align: left }`,
 * so a totals row sat left-aligned beneath right-aligned figures — in a payroll
 * register, which is the one table where the columns have to line up, because
 * lining them up is how anybody checks the addition by eye.
 */
table.grid td.num, table.grid th.num,
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

@container app (max-width: 720px) {
  /*
   * Nothing to do here any more, and that is the point of the change. (DROP-1)
   *
   * Four rules stood here to turn the 200px column into a scrolling strip
   * along the top at tablet width — the layout had to become a different thing
   * to fit. A dropdown is already the narrow-window answer, so it fits at
   * every width with no rule of its own, and the label simply wraps above the
   * box. `ui-zoom.test.ts` keeps this a CONTAINER query, so it follows the
   * text size as everything else here does.
   */
  .window-sections { flex-wrap: wrap; gap: 6px 10px; }
}

/* ── Signing in. Its own screen: no rail, no switcher, nothing to click. ── */
.signin {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--bg);
  padding: 20px;
  z-index: 10;
}

.signin-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 28px 30px 24px;
  width: 100%;
  max-width: 380px;
  box-shadow: 0 1px 3px var(--shadow);
}

.signin-card h1 { font-size: 20px; margin: 0 0 4px; font-weight: 600; }
.signin-card h2 { font-size: 15px; margin: 0 0 6px; font-weight: 600; }
.signin-card > p { margin: 0 0 20px; font-size: 13px; }
.signin-card #signin-code p { margin: 0 0 16px; font-size: 13px; }

button.wide { width: 100%; margin-top: 6px; }
button.link {
  background: none;
  border: 0;
  color: var(--accent-text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  padding: 6px 2px;
}
button.link:hover { text-decoration: underline; }

/* Hug the buttons rather than stretching the border across the card. */
.signin-lang { margin: 22px auto 0; width: max-content; }

.setup-key {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 15px;
  letter-spacing: 1px;
  background: var(--tint);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 10px 12px;
  word-break: break-all;
}

ul.codes {
  list-style: none;
  padding: 0;
  margin: 14px 0;
  display: grid;
  grid-template-columns: repeat(2, max-content);
  gap: 6px 28px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 14px;
}

/* A choice the reader has to make, rather than one already made for them:
   the option and its explanation sit together, and neither is preselected. */
.choice {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 12px 14px;
  margin: 8px 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
}
.choice:hover { background: var(--tint); }
.choice input { margin-top: 3px; }
.choice strong { display: block; font-weight: 600; }
.choice small { display: block; color: var(--ink-soft); margin-top: 3px; max-width: 60ch; }

.week { display: flex; flex-wrap: wrap; gap: 14px; margin: 0 0 12px 34px; padding: 10px 12px; }
.week label { display: flex; align-items: center; gap: 6px; }

/* A requirement that belongs to a GROUP of fields rather than to any one of
   them — "at least one of these three". Said once, above the row, because
   marking each box "required" would be three lies. */
.group-req {
  margin: 14px 0 4px;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-soft);
}

/* An item that has finished. Still listed, because somebody asking why a
   deduction appeared in March needs to see that it ran January to June — but
   quietened, so the ones still running read first. */
table.grid tbody tr.is-ended td { color: var(--ink-soft); }
table.grid td .stop-date { margin-right: 8px; padding: 3px 6px; font-size: 12px; }

/* ── The TD59, laid out as the form lays it out ───────────────────────────── */

/*
 * TD59 B2 — the exemption assessment. The questions on the left, the result
 * and the Save / Accept buttons on the right, one column when the app is narrow.
 * Every colour is a variable, so the dark theme needs nothing of its own here.
 */
details.relief-assess { margin: 12px 0 18px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }
details.relief-assess > summary { cursor: pointer; padding: 10px 14px; font-weight: 600; color: var(--accent-text); }
details.relief-assess > p.muted { margin: 0 14px 10px; max-width: 80ch; }
.relief-assess-body { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: 18px; padding: 0 14px 14px; }
.relief-assess-form h4, .relief-assess-result h4 { margin: 14px 0 4px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--accent-text); }
.relief-assess-q { border-top: 1px solid var(--line); padding: 8px 0; min-width: 0; }
.relief-assess-q > label { display: block; font-weight: 600; font-size: 13px; margin-bottom: 4px; }
.relief-assess-q.idle > label { font-weight: 400; color: var(--ink-soft); }
.relief-assess-q.idle > p { margin: 0; font-size: 12px; }
.relief-assess-q.needed > label { color: var(--warn); }
.relief-assess-help { font-size: 12px; color: var(--ink-soft); margin: 0 0 6px; max-width: 80ch; }
.relief-assess-src { font-size: 11px; color: var(--ink-soft); margin: 4px 0 0; }
.relief-assess-years { display: flex; flex-wrap: wrap; gap: 6px; }
.relief-assess-years label { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; border: 1px solid var(--line); border-radius: 4px; padding: 2px 6px; font-variant-numeric: tabular-nums; }
.relief-assess-rows input, .relief-assess-rows select { width: 100%; min-width: 110px; }
.relief-assess-side { min-width: 0; align-self: start; position: sticky; top: 8px; }
.relief-assess-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 12px; }
.relief-assess-actions .relief-assess-status { flex-basis: 100%; margin: 0; font-size: 12px; }
.relief-assess-actions .notice { flex-basis: 100%; margin: 0; }
.relief-assess-needs { font-size: 12px; color: var(--warn); }
.relief-assess-result table.grid td { vertical-align: top; font-size: 12px; }
@container app (max-width: 860px) {
  .relief-assess-body { grid-template-columns: minmax(0, 1fr); }
  .relief-assess-side { position: static; }
}

table.td59 { width: 100%; border-collapse: collapse; font-size: 13px; }
table.td59 th, table.td59 td { padding: 6px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
table.td59 thead th { text-align: left; font-weight: 600; color: var(--ink-soft); font-size: 12px; }
table.td59 td.num, table.td59 thead th:nth-child(3), table.td59 thead th:nth-child(4) { text-align: right; }
table.td59 td.num input, table.td59 td.num select { width: 100%; min-width: 96px; text-align: right; }
table.td59 .td59-code { font-variant-numeric: tabular-nums; color: var(--ink-soft); white-space: nowrap; width: 1%; }
table.td59 td small { display: block; color: var(--ink-soft); font-size: 11px; margin-top: 2px; }

/* A part heading is a row, so the table keeps one column grid throughout. */
table.td59 tr.td59-part th {
  text-align: left;
  background: var(--tint);
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding-top: 10px;
}

/* The lines nobody types into. Set apart so a total is never mistaken for a box. */
table.td59 tr.td59-total { background: var(--tint-soft); font-weight: 600; }
table.td59 tr.td59-sub td:nth-child(2) { padding-left: 24px; color: var(--ink-soft); }

.note-btn {
  font-size: 11px;
  padding: 2px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  background: var(--panel);
  color: var(--ink-soft);
  cursor: pointer;
  white-space: nowrap;
}
.note-btn:hover { background: var(--tint); color: var(--ink); }

/* ── The notes window ────────────────────────────────────────────────────── */

dialog.note-window {
  max-width: 680px;
  width: calc(100% - 32px);
  max-height: 80vh;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  color: var(--ink);
  background: var(--panel);
}
dialog.note-window::backdrop { background: var(--backdrop); }
.note-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  background: var(--panel);
}
.note-head h3 { margin: 0; font-size: 14px; font-weight: 600; }
.note-body { padding: 4px 20px 20px; overflow-y: auto; max-height: calc(80vh - 64px); }
.note-body h4 { margin: 16px 0 6px; font-size: 12px; color: var(--ink-soft); font-weight: 600; }
.note-body p { margin: 0 0 10px; font-size: 13px; line-height: 1.55; }

/* A line's notes button sits in its own column. */
table.td59 td.td59-tools { white-space: nowrap; }

/* ── A locked box ─────────────────────────────────────────────────────────── */
/*
 * The read view of a field. The same box as the edit view so the screen does
 * not change shape when Edit is pressed — quieter, and not typeable.
 */
.field.locked input {
  background: var(--tint);
  border-color: var(--line);
  color: var(--ink);
  cursor: default;
}
.field.locked input:focus { outline: none; border-color: var(--line); }

/* ── Proving it is you again ──────────────────────────────────────────────── */
/*
 * Narrow, and styled from `note-window` rather than `.window` — `.window` sets
 * `display: grid`, which overrides the browser's own
 * `dialog:not([open]) { display: none }` and leaves a closed dialog laid out on
 * the page. A password box rendered behind a prompt nobody opened.
 */
dialog.stepup-window { max-width: 420px; }
dialog.stepup-window h2 { margin: 16px 0 4px; font-size: 15px; font-weight: 600; }
dialog.stepup-window .actions { margin-top: 18px; }

/* ── The unsaved-changes question ─────────────────────────────────────────── */
dialog.unsaved-window { max-width: 480px; }
dialog.unsaved-window .note-body p { margin: 4px 0 0; }
dialog.unsaved-window .actions { margin-top: 20px; flex-wrap: wrap; }

/* The TD59 is a form: its boxes are open from the moment it opens. */
table.td59 td.num input[type="text"],
table.td59 td.num input[type="number"],
table.td59 td.num input[type="date"],
table.td59 td.num select {
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  padding: 4px 6px;
  font: inherit;
  background: var(--panel);
}
table.td59 td.num input:focus, table.td59 td.num select:focus {
  outline: 2px solid var(--accent-text);
  outline-offset: -1px;
  border-color: var(--accent-text);
}

/* ── The eligibility assessment ───────────────────────────────────────────── */

#assess-questions .field { margin-bottom: 16px; }
#assess-questions .field input, #assess-questions .field select { width: 100%; }

/* The verdict, which is the answer — the amount is the footnote to it. */
.verdict { border-radius: 6px; padding: 12px 14px; margin: 18px 0 4px; font-size: 13px; }
.verdict strong { display: block; font-size: 14px; margin-bottom: 4px; }
.verdict p { margin: 0; }
.verdict.eligible { background: var(--good-bg); color: var(--good); border: 1px solid var(--good-line); }
.verdict.not_eligible { background: var(--bad-bg); color: var(--bad); border: 1px solid var(--bad-line); }
.verdict.undecided { background: var(--warn-bg); color: var(--warn); border: 1px solid var(--warn-line); }

.assess-btn { border-color: var(--accent-text); color: var(--accent-text); }
.assess-btn:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
#assess-working h4 { margin: 18px 0 6px; font-size: 12px; color: var(--ink-soft); font-weight: 600; }
table.assess-working { width: 100%; border-collapse: collapse; font-size: 13px; }
table.assess-working td { padding: 5px 8px; border-bottom: 1px solid var(--line); }
table.assess-working td.num { text-align: right; white-space: nowrap; }
table.assess-working tr.assess-emphasis td { color: var(--warn); }
table.assess-working tr.assess-total td { font-weight: 600; background: var(--tint-soft); }

/* B10's answers, said in a line rather than asked in four boxes. */
table.td59 small.td59-household { color: var(--accent-text); font-size: 12px; margin-top: 3px; }

/* The tax year the TD59 is for — a choice, not today's date. */
.field.td59-year { max-width: 260px; margin-bottom: 18px; }

/**
 * ── The Library ──
 *
 * A shelf, not a grid. Each document is a block with its title, the line
 * somebody wrote about what it settles, and its two buttons on the right — a
 * table would put "2.7 MB" in a column of its own and make the note, which is
 * the useful part, a truncated cell.
 */
.library-row {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  justify-content: space-between;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}
.library-main { min-width: 0; }
.library-main p { margin: 4px 0 0; }
.library-main a { display: block; margin-top: 4px; font-size: 0.8rem; word-break: break-all; }
.library-meta { font-size: 0.8rem; color: var(--ink-soft); }
.library-actions { display: flex; gap: 8px; flex: 0 0 auto; }
#library-filter { max-width: 220px; }

/* ── Users ─────────────────────────────────────────────────────────────── */
#user-form { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line); max-width: 640px; }
#user-grants-table td { padding: 6px 10px; }
#user-grants-table select { width: auto; min-width: 140px; }
#users-table tr.inactive td { color: var(--ink-soft); }
#users-table td.actions-cell { text-align: right; white-space: nowrap; }
.pill.off { background: var(--bad-bg); color: var(--bad); }
.pill.warn { background: var(--warn-bg); color: var(--warn); }
ul.history { list-style: none; padding: 0; margin: 8px 0 0; font-size: 13px; }
ul.history li { padding: 4px 0; border-bottom: 1px solid var(--line); }

/* A second, quieter line under a table value. */
table.grid td .small { font-size: 12px; margin-top: 2px; }
.history-table td { vertical-align: top; }
.history-table td:first-child { white-space: nowrap; }

/* ── Starting part-way through a year ─────────────────────────────────── */
#start-panel { margin-top: 18px; }
#start-panel .inline-field { max-width: 180px; }
#start-status:empty { display: none; }
#opening-form fieldset { margin-top: 14px; }
#opening-form .grid-2 { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 0 18px; }
#opening-form .grid-2 input { text-align: right; font-variant-numeric: tabular-nums; }

/* "Mark all as checked" on a company's first payroll. */
#review-check-all p { margin: 0 0 8px; }
/* Under the period list, out of the row's flow so the fields stay aligned. */
.field:has(> #period-start-note) { position: relative; }
#period-start-note { position: absolute; left: 0; top: 100%; margin-top: 4px; white-space: nowrap; }
#open-period-form:has(#period-start-note:not([hidden])) { padding-bottom: 22px; }

/* Employees from an Excel sheet. */
.import-panel { border-top: 1px solid var(--line, #e2e5ea); margin-top: 16px; padding-top: 12px; }

/* Importing several people, folded away above the form that adds one. Closed
   it is a single line, which is right: adding one person is the common case. */
#import-entry { padding: 14px 22px; margin-bottom: 16px; }
#import-entry > summary {
  cursor: pointer; font-size: 13.5px; font-weight: 600; color: var(--accent-text);
  width: fit-content; list-style-position: inside;
}
#import-entry > summary:hover { color: var(--accent-text-strong); }
#import-entry[open] > summary { margin-bottom: 4px; }
#import-entry #import-panel { border-top: 0; margin-top: 0; padding-top: 0; }
.import-panel .panel-head h3 { margin: 0; }
#import-summary p { margin: 0 0 4px; }
#import-summary p:last-child { margin-bottom: 0; }
#import-table tr.import-bad td:first-child { color: var(--bad, #9c2a2a); font-weight: 600; }
.panel-head .actions { display: flex; gap: 8px; }

/* Deleting a company. */
.danger-panel { border-color: var(--bad); }
.danger-panel h3 { color: var(--bad); margin-top: 0; }
#delete-company-form { margin-top: 12px; }
#delete-company-form .field input { max-width: 420px; }

/* Opening balances by Excel, above the people table. */
.opening-import { margin: 8px 0 16px; padding: 12px; border: 1px dashed var(--line); border-radius: 6px; }
.opening-import p { margin-top: 0; }
#opening-import-summary p { margin: 0 0 4px; }
#opening-import-table tr.import-bad td:first-child { color: var(--bad); font-weight: 600; }

/* ─── The four steps of a payroll month ─── */
.steps { display: flex; flex-wrap: wrap; gap: 6px; list-style: none; margin: 0 0 8px; padding: 0; counter-reset: none; }
.steps li { flex: 1 1 150px; }
.steps button {
  width: 100%; display: flex; align-items: center; gap: 8px; text-align: left;
  padding: 8px 12px; border-radius: 6px; font-size: 13px; cursor: pointer;
  background: var(--panel); border: 1px solid var(--line); color: var(--ink-soft);
}
.steps .step-no {
  flex: none; width: 22px; height: 22px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center; font-size: 12px; font-weight: 600;
  background: var(--bg); border: 1px solid var(--line-strong); color: var(--ink-soft);
}
.steps li.done button { color: var(--good); border-color: var(--good-line); background: var(--good-bg); }
.steps li.done .step-no { background: var(--good); border-color: var(--good); color: #fff; font-size: 0; }
.steps li.done .step-no::after { content: '✓'; font-size: 12px; }
.steps li.current button { color: var(--accent-text-strong); border-color: var(--accent-text); background: var(--accent-tint); font-weight: 600; }
.steps li.current .step-no { background: var(--accent); border-color: var(--accent); color: #fff; }
.steps-next { max-width: none; margin: 0 0 16px; font-size: 13px; color: var(--ink); }

/* ─── Changes due this period ─── */
#changes-panel { margin-top: 22px; }
#changes-table td:nth-child(4) { color: var(--ink-soft); }
#changes-table td:last-child { text-align: right; white-space: nowrap; }


/* ─── Loans ─── */
.loan-form { border-top: 1px solid var(--line); margin-top: 18px; padding-top: 12px; }
tr.loan-details > td { background: var(--bg); padding: 12px 16px; }
tr.loan-details h4 { margin: 0 0 8px; font-size: 13px; }
table.grid.compact td, table.grid.compact th { padding: 6px 10px; }
#loans-table td.actions-cell { text-align: right; }
.inline-check { display: inline-flex; gap: 6px; align-items: center; font-size: 13px; margin: 4px 0 10px; }
.loan-form .grid-2 { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 0 18px; }

/* ─── Who is away: a month across, a person down ───                    (B3)
   The marks are what say which leave it is; the tints are a second signal and
   never the only one, because a calendar read by colour alone is a calendar
   half the office cannot read. */
table.grid.away-grid { font-size: 12px; }
table.grid.away-grid tbody tr { cursor: default; }
table.grid.away-grid th.away-day, table.grid.away-grid td.away-day {
  text-align: center; padding: 6px 2px; min-width: 24px;
}
table.grid.away-grid .away-weekend { background: var(--tint); }
table.grid.away-grid td.away-on { background: var(--good-bg); font-weight: 600; }
table.grid.away-grid td.away-asked { background: var(--warn-bg); font-style: italic; }
table.grid.away-grid td:first-child { white-space: nowrap; }

/* ─── Phones ───
   Tables slide inside their own box (see wrapTables in app.js), so the page
   never scrolls sideways. Buttons wrap two to a row and are big enough to
   press with a thumb. */
.table-scroll { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
@container app (max-width: 560px) {
  /* The bar itself is handled with the rule that wraps it, well above this.
     All that is left here is the company's name, which gets a little more room
     on a phone because its row has nothing else competing for the width. */
  .entity-switch select { max-width: 170px; }
  main { padding: 12px; }
  .panel, .panel.wide { padding: 16px 14px; }
  .actions button { flex: 1 1 calc(50% - 5px); min-height: 44px; padding: 8px 10px; }
  .panel-head .actions button { flex: 0 0 auto; min-height: 40px; }
  table.grid td, table.grid th { padding: 8px 8px; white-space: nowrap; }
  .steps li { flex: 1 1 calc(50% - 6px); }
  .inline-form .field { flex: 1 1 100%; }
  .inline-form button { flex: 1 1 100%; min-height: 44px; }
}

/* ─── The register on a phone: one card per person ───
   Name and net pay on the first line, where the eye lands; the other figures
   underneath, each with its label. Nothing to slide sideways. The columns keep
   their order in the page, so everything that reads them by position still
   does; only the drawing changes. */
@container app (max-width: 560px) {
  #register-table thead { display: none; }
  #register-table, #register-table tbody, #register-table tfoot { display: block; width: 100%; }
  #register-table tr { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; padding: 10px 2px; border-bottom: 1px solid var(--line); }
  #register-table tfoot tr { border-top: 2px solid var(--line-strong); border-bottom: 0; }
  #register-table td, #register-table th { border: 0; padding: 0; white-space: nowrap; text-align: left; }
  #register-table tr > :nth-child(1) { order: 1; color: var(--ink-soft); min-width: 1.6em; }
  #register-table tr > :nth-child(2) { order: 2; font-weight: 600; flex: 0 1 auto; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
  /* A long name wraps within its own space; it never pushes net pay onto the next line. */
  #register-table tr > :nth-child(3) { order: 3; font-weight: 600; flex: 1 1 0; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
  #register-table tr > :nth-child(8) { order: 4; font-weight: 700; font-size: 15px; margin-left: auto; flex: 0 0 auto; }
  /* The line break between the first line and the figures. */
  #register-table tr::after { content: ''; order: 5; flex-basis: 100%; height: 0; }
  #register-table tr > :nth-child(n+4):not(:nth-child(8)) { order: 6; font-size: 12px; color: var(--ink-soft); }
  #register-table tr > :nth-child(n+4):not(:nth-child(8))::before { content: attr(data-label) ' '; }
  #register-table tfoot tr > :nth-child(2), #register-table tfoot tr > :nth-child(3) { display: none; }
}

.range-help { flex: 1 1 100%; }
#view-payroll .panel-head .actions { margin-top: 0; }

/* The Social Insurance return check, above the list of payrolls. */
#return-result { margin-bottom: 18px; }
#return-result table { margin-bottom: 12px; }

/* Reports: a ready file, and the reasons one is not. */
#view-reports strong.good { color: var(--good); }
#view-reports strong.bad { color: var(--bad); }
#view-reports ul.reasons { margin: 4px 0 0; padding-left: 18px; font-size: 0.9em; color: var(--ink-soft); }
#view-reports h3 { margin-top: 22px; }
#view-reports table.grid { table-layout: fixed; width: 100%; }
#view-reports table.grid th:nth-child(1) { width: 32%; }
#view-reports table.grid th:nth-child(2) { width: 18%; }
#view-reports table.grid th:nth-child(4) { width: 110px; }
#view-reports table.grid td:nth-child(4) { text-align: right; }

/* The month's entries grid: narrow inputs, one row per person. */
table.month-grid td { padding: 4px 6px; vertical-align: top; }
table.month-grid tbody tr { cursor: default; }
table.month-grid input { width: 100%; min-width: 80px; }
table.month-grid td.num input { text-align: right; min-width: 72px; }
table.month-grid input[type="date"] { min-width: 130px; }
table.month-grid input.bad { border-color: var(--bad); background: var(--bad-bg, #fdecec); }
table.month-grid tr.month-check td { font-size: 12px; padding-top: 0; color: var(--ink-soft); }
table.month-grid tr.month-check.has-problem td { color: var(--bad); }
table.month-grid .small { font-size: 11px; }
table.month-grid td:nth-child(2) { min-width: 170px; }
table.month-grid td:nth-child(11) input { min-width: 160px; }

/* Under a block's buttons while typing has not been saved. */
.edit-form .unsaved-note { color: var(--warn, #9a6700); margin: 6px 0 0; }
#month-panel .unsaved-note { color: var(--warn, #9a6700); margin: 6px 0 0; }

/* ── The dashboard ──────────────────────────────────────────────────────────
 *
 * A strip of periods across the top, then the work queue and this period's
 * figures side by side. Two columns on a desk, stacked on a phone.
 *
 * Every state carries a word and a mark as well as a colour. The reason is in
 * the palette: --bad and --warn are nearly the same colour to a red-green
 * colourblind reader — about one man in twelve — and only just separable to
 * anybody else at a glance. A status told by hue alone is a status told to
 * some of the people some of the time.
 */

.dash-head {
  display: flex; align-items: baseline; gap: 10px;
  margin: 22px 0 10px; flex-wrap: wrap;
}
.dash-head h3 { margin: 0; font-size: 15px; }

/* ─── The year in numbers and graphs (DASH-2) ─────────────────────────────
   What replaced the strip of period boxes. Numbers first, then the months as
   bars, then where the cost goes and the cost by department. One axis at a
   time; last year beside each month as a paler bar of the same measure; a key
   under every chart with two series, so nothing is told by colour alone. */
.dash-year-pick { margin-left: auto; display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink-soft); }
.dash-year-pick select { width: auto; min-width: 90px; }
.kpis { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
#dash-ins-draft { margin: 8px 0 0; }

.dash-chart-panel {
  margin-top: 18px; padding: 16px 18px 14px;
  border: 1px solid var(--line); border-radius: 4px; background: var(--panel);
}
.chart-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.chart-head h3 { margin: 0; font-size: 15px; }
.dash-chart-panel > p.muted { margin: 4px 0 12px; font-size: 12.5px; }
.seg-pick { display: inline-flex; border: 1px solid var(--line-strong); border-radius: 4px; overflow: hidden; }
.seg-pick button {
  background: var(--panel); color: var(--ink-soft); border: 0; border-left: 1px solid var(--line-strong);
  padding: 5px 11px; font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.seg-pick button:first-child { border-left: 0; }
.seg-pick button.on { background: var(--accent); color: #fff; }
.seg-pick button:not(.on):hover { background: var(--tint); color: var(--ink); }
.chart-compare { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-soft); margin-left: auto; }
.chart-compare input { width: auto; margin: 0; }

/* The plot: a definite height, or the percentage heights inside it resolve to
   nothing and the chart draws blank while every element is still there. */
.mchart { position: relative; height: 200px; margin: 0 0 0 64px; }
.mgrid { position: absolute; inset: 0 0 22px 0; pointer-events: none; }
.mgrid .gl { position: absolute; left: 0; right: 0; border-top: 1px solid var(--line); }
.mgrid .gl span {
  position: absolute; right: calc(100% + 8px); top: -8px;
  font-size: 11px; color: var(--ink-soft); white-space: nowrap; font-variant-numeric: tabular-nums;
}
.mplot { position: absolute; inset: 0; display: flex; gap: 4px; }
.mslot {
  flex: 1; min-width: 0; position: relative; display: flex; flex-direction: column;
  background: none; border: 0; padding: 0; font: inherit; color: inherit; border-radius: 4px;
}
button.mslot { cursor: pointer; }
.mslot:hover, .mslot:focus-visible { background: var(--tint); }
.mbars { flex: 1; display: flex; align-items: flex-end; justify-content: center; gap: 2px; min-height: 0; }
.mbar { height: 100%; width: 100%; max-width: 26px; display: flex; flex-direction: column; justify-content: flex-end; gap: 2px; }
.mchart.compare .mbar { max-width: 18px; }
.mbar .part { width: 100%; }
.mbar .part:first-child { border-radius: 4px 4px 0 0; }
.mbar.now .part.pay { background: var(--accent); }
.mbar.now .part.extra { background: var(--accent-soft); }
.mbar.now.draft .part.pay { background: var(--line-strong); }
.mbar.now.draft .part.extra { background: var(--line); }
/* Last year: an outline, not a fill, so it can never be read as a draft. */
.mbar.then .part { background: transparent; border: 1.5px solid var(--ink-soft); border-bottom: 0; box-sizing: border-box; }
.mslot:hover .mbar.now .part.pay { background: var(--accent-dark); }
.mlabel { height: 22px; line-height: 22px; text-align: center; font-size: 11px; color: var(--ink-soft); }
.mslot .tip {
  position: absolute; bottom: calc(100% - 14px); left: 50%; transform: translate(-50%, -6px);
  background: var(--rail); color: #fff; font-size: 12px; padding: 4px 8px;
  border-radius: 4px; white-space: nowrap; opacity: 0; pointer-events: none;
  transition: opacity .12s; z-index: 3;
}
.mslot:first-child .tip { left: 0; transform: translate(0, -6px); }
.mslot:last-child .tip { left: auto; right: 0; transform: translate(0, -6px); }
.mslot:hover .tip, .mslot:focus-visible .tip { opacity: 1; }
.trend-key .swatch.then { background: transparent; border: 1.5px solid var(--ink-soft); box-sizing: border-box; }

.dash-graphs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 20px; margin-top: 18px;
}
.dash-graphs .dash-panel {
  padding: 16px 18px; border: 1px solid var(--line); border-radius: 4px; background: var(--panel);
}
ul.hbars { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
ul.hbars li { display: grid; grid-template-columns: minmax(90px, 30%) 1fr auto; align-items: center; gap: 10px; font-size: 12.5px; }
ul.hbars .who { overflow-wrap: anywhere; }
ul.hbars .track { height: 12px; }
ul.hbars .fill { display: block; height: 100%; min-width: 2px; background: var(--accent); border-radius: 0 4px 4px 0; }
ul.hbars .amt { color: var(--ink-soft); white-space: nowrap; font-variant-numeric: tabular-nums; }

.dash-cols {
  display: grid; grid-template-columns: 1.3fr 1fr;
  gap: 26px; margin-top: 26px;
}
.dash-col h3 { margin: 0 0 10px; font-size: 15px; }
@container app (max-width: 860px) { .dash-cols { grid-template-columns: 1fr; gap: 20px; } }

ul.queue { list-style: none; margin: 0; padding: 0; }
ul.queue li {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 0; border-top: 1px solid var(--line);
}
ul.queue li:first-child { border-top: 0; }
.queue .pip {
  flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%;
  font-size: 12px; font-weight: 700; line-height: 20px; text-align: center;
}
.queue .pip.late { background: var(--bad-bg); color: var(--bad); }
.queue .pip.due { background: var(--warn-bg); color: var(--warn); }
.queue .pip.note { background: var(--accent-tint); color: var(--accent-text); }
.queue .qtext { flex: 1; }
.queue .qtext button {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--accent-text); font-weight: 600; cursor: pointer; text-align: left;
}
.queue .qtext button:hover { text-decoration: underline; }
/* A line with nowhere to go — see the renderer. It keeps the weight of the
   buttons beside it so the list still reads as one list, and takes the ordinary
   ink so nothing about it offers to be pressed. */
.queue .qtext .qflat { font-weight: 600; color: var(--ink); }
.queue .qtext .sub { display: block; color: var(--ink-soft); font-size: 12.5px; }

/*
 * Bordered cells rather than a background showing through 1px gaps: a row that
 * does not fill its last column left the container's colour on screen as a
 * grey block, which reads as a sixth figure nobody filled in.
 */
.stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 8px;
}
.stat {
  background: var(--panel); padding: 11px 13px;
  border: 1px solid var(--line); border-radius: 4px;
}
.stat .k {
  font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-soft); font-weight: 700;
}
.stat .v { font-size: 20px; font-weight: 700; margin-top: 3px; font-variant-numeric: tabular-nums; }
.stat .d { font-size: 12px; color: var(--ink-soft); margin-top: 1px; }
/* A rise in cost is not an error, so it is not red. It is worth noticing, which
   is what the arrow is for. */
.stat .d.up::before { content: '▲ '; }
.stat .d.down::before { content: '▼ '; }

/* ── The panels that stay quiet ─────────────────────────────────────────────
 *
 * Each is absent — not collapsed, not greyed — when it has nothing to say.
 * A toggle would be a thing to remember; an empty panel would be a thing to
 * scan past. Nine blocks fit on one screen only because most days most of them
 * are not there.
 */
.dash-panels {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px; margin-top: 26px;
}
.dash-panel { min-width: 0; }
.dash-panel.wide { grid-column: 1 / -1; }
.dash-panel h3 { margin: 0 0 4px; font-size: 15px; }
.dash-panel h3 + p.muted { margin: 0 0 10px; font-size: 12.5px; }
.dash-panel table { margin-top: 8px; }

/* The state words. A chip carries the word; the colour only agrees with it. */
.chip {
  display: inline-block; font-size: 11.5px; font-weight: 700;
  padding: 2px 7px; border-radius: 10px; white-space: nowrap;
}
.chip.good { background: var(--good-bg); color: var(--good); }
.chip.warn { background: var(--warn-bg); color: var(--warn); }
.chip.bad { background: var(--bad-bg); color: var(--bad); }
.chip.idle { background: var(--tint); color: var(--ink-soft); }


/* A breakdown row under the figure it adds up to — the eight contributions
   under the one Social Insurance payment. Indented and quieter than the row
   above it, so the list still reads as a list of payments. */
.grid tr.sub td { color: var(--ink-soft); font-size: 12.5px; border-top: 0; }
.grid tr.sub td:first-child { padding-left: 24px; }
.grid tr.total th, .grid tr.total td { border-top: 2px solid var(--line-strong); font-weight: 600; }

/* ─── The manual and the handbook ─────────────────────────────────────────
   Contents beside the chapter, and the chapter set to a reading measure. A
   page of prose at the full width of the panel is a page nobody finishes. */
.help-layout { display: flex; gap: 28px; align-items: flex-start; }
/* Sticky under the module bar (52px), so a chapter that runs to three screens
   still has its contents beside it rather than somewhere above. */
.help-nav { flex: 0 0 210px; min-width: 0; position: sticky; top: 64px;
  max-height: calc(100vh - 84px); overflow-y: auto; }
.help-nav h3 { font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-soft); margin: 18px 0 6px; font-weight: 600; }
.help-nav h3:first-child { margin-top: 0; }
.help-nav button {
  display: block; width: 100%; text-align: left; background: none; border: 0;
  border-left: 2px solid transparent; padding: 6px 10px; font-size: 13px;
  color: var(--ink); cursor: pointer; border-radius: 0;
}
.help-nav button:hover { background: var(--bg); }
.help-nav button[aria-current="true"] { border-left-color: var(--accent-text); color: var(--accent-text); font-weight: 600; }

.help-body { flex: 1; min-width: 0; max-width: 68ch; }
.help-body:focus { outline: none; }
.help-body h2 { font-size: 17px; margin: 0 0 4px; font-weight: 600; }
.help-body h3 { font-size: 14px; margin: 26px 0 8px; font-weight: 600; }
.help-body h4 { font-size: 13px; margin: 18px 0 6px; font-weight: 600; color: var(--ink-soft); }
.help-body p { margin: 0 0 12px; line-height: 1.6; }
.help-body .chapter-about { color: var(--ink-soft); margin-bottom: 18px; }
.help-body ul, .help-body ol { margin: 0 0 12px; padding-left: 20px; }
.help-body li { margin-bottom: 6px; line-height: 1.6; }
.help-body code { background: var(--bg); border: 1px solid var(--line); border-radius: 3px;
  padding: 0 4px; font-size: 12.5px; }
.help-body table { width: 100%; border-collapse: collapse; margin: 0 0 16px; font-size: 13px; }
.help-body th { text-align: left; border-bottom: 1px solid var(--line-strong); padding: 7px 8px; font-weight: 600; }
.help-body td { border-bottom: 1px solid var(--line); padding: 7px 8px; vertical-align: top; }
/* A note is the author stopping to say "mind this", so it is set apart rather
   than left as one more paragraph among the paragraphs. */
.help-body .help-note {
  border-left: 3px solid var(--line-strong); background: var(--bg);
  padding: 10px 14px; margin: 0 0 14px; color: var(--ink-soft); line-height: 1.6;
}

@container app (max-width: 820px) {
  .help-layout { flex-direction: column; gap: 16px; }
  .help-nav { flex: none; width: 100%; }
}

/* ─── The period, drawn as two hundred-percent bars ───────────────────────
   Marks are thin, the separating is done by the SURFACE rather than by a
   border — a 2px gap between touching segments — and the ends are rounded at
   the outside of the bar only, because the inside of a stack has no end. */
/* The two blocks share ROWS, not just a row: their sub-lines are different
   lengths, and without this the second bar sits half a line above the first
   and the totals do not line up with each other. Subgrid makes the heading,
   the sub-line, the total, the bar and the legend align across both columns
   however the text wraps. */
.splits {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  grid-template-rows: auto auto auto auto auto;
  gap: 4px 28px;
}
.split { display: grid; grid-template-rows: subgrid; grid-row: span 5; min-width: 0; }
.split h4 { font-size: 13px; margin: 0; font-weight: 600; align-self: end; }
.split > p.muted { font-size: 12px; margin: 4px 0 0; max-width: 46ch; line-height: 1.5; }
/* The whole, as a figure. A bar of shares with no total is a picture of a
   proportion nobody can price. */
.split-total { font-size: 19px; font-weight: 600; margin: 12px 0 0; letter-spacing: -0.01em; align-self: end; }

.split-bar { display: flex; gap: 2px; height: 22px; margin: 8px 0 12px; }
.split-bar .seg {
  /* Proportional by flex-grow, never by a percentage width: the 2px gaps come
     out of the space first and the rest divides exactly, so the bar always
     ends where the track ends. */
  flex-basis: 0;
  /* A slice too small to see is a slice that is not there. It is drawn at its
     smallest readable width and the legend carries the true figure. */
  min-width: 3px;
  background: var(--seg);
  border-radius: 0;
}
.split-bar .seg:first-child { border-radius: 4px 0 0 4px; }
.split-bar .seg:last-child { border-radius: 0 4px 4px 0; }
.split-bar .seg:only-child { border-radius: 4px; }

/* The legend is not decoration here: three of the five fills sit below 3:1
   against white, so the NAME and the FIGURE are what carry them. */
.split-key { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.split-key li { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; }
.split-key .swatch {
  flex: 0 0 auto; width: 10px; height: 10px; border-radius: 2px;
  background: var(--seg); transform: translateY(1px);
}
/* Text wears text tokens, never the series colour — the swatch beside it
   carries the identity. */
.split-key .who { color: var(--ink); min-width: 0; overflow-wrap: anywhere; }
.split-key .amt { margin-left: auto; color: var(--ink-soft); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* The key under a chart: every series named, never colour alone. */
.trend-key { list-style: none; display: flex; gap: 16px; margin: 10px 0 0; padding: 0; }
.trend-key li { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-soft); }
.trend-key .swatch { width: 10px; height: 10px; border-radius: 2px; }
.trend-key .swatch.gross { background: var(--accent); }
.trend-key .swatch.extra { background: var(--accent-soft); }

/* ─── The year in figures ─────────────────────────────────────────────────
   Folded away by default: it is a reference, not a headline, and the strip
   above it already answers the question people open a client to ask. */
.year-figures { margin: 6px 0 18px; }
.year-figures summary {
  cursor: pointer; font-size: 13px; font-weight: 600; color: var(--accent-text);
  padding: 4px 0; width: fit-content;
}
.year-figures summary:hover { color: var(--accent-text-strong); }
.year-figures table { margin-top: 8px; }
.year-figures .num, .year-figures td.num { text-align: right; font-variant-numeric: tabular-nums; }
/* A period still being worked on, so its row is quieter than the closed ones. */
.year-figures tr.draft td { color: var(--ink-soft); font-style: italic; }
.year-figures tr.total td { border-top: 2px solid var(--line-strong); font-weight: 600; }
.year-figures .note { font-size: 12px; color: var(--ink-soft); margin: 8px 0 0; }

/* ─── The run's documents, behind one word ────────────────────────────────
   A drawer rather than a row: the six were competing with Approve for the
   same strip of screen, and only one of them is ever the next thing to do. */
.documents {
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel);
  padding: 14px 18px; margin: 0 0 18px;
}
.documents .panel-head { margin-bottom: 4px; }
.documents .panel-head h3 { margin: 0; font-size: 14px; }
.documents > p.muted { font-size: 12.5px; margin: 0 0 12px; max-width: 74ch; line-height: 1.55; }
.documents .actions { margin: 0; }

/* The reversed runs, folded under the list they are not part of. */
.reversed-fold { margin-top: 16px; }
.reversed-fold > summary {
  cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--ink-soft);
  width: fit-content; padding: 4px 0;
}
.reversed-fold > summary:hover { color: var(--ink); }
.reversed-fold table { margin-top: 10px; }
.reversed-fold tbody td { color: var(--ink-soft); }

/* The key, the one time it is shown. (LINK-3)
   Monospace and breakable: it is 47 characters of base64url that somebody has
   to select and copy exactly, and a proportional font makes that a transcription
   error waiting to happen. Word-break rather than a scrollbar, so the whole of
   it is on screen at once — half a key copied is a link that fails with the
   same message as a wrong one. */
.key-once {
  display: block;
  margin-top: 8px;
  padding: 10px 12px;
  background: var(--tint);
  border: 1px solid var(--line);
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.95em;
  word-break: break-all;
  user-select: all;
}

/**
 * ── Somebody who has asked their machine for less movement ────────────────
 *
 * There is not much to cancel here, and that is worth writing down rather than
 * leaving a reader to discover: this stylesheet has ONE transition in it — the
 * tooltip over a bar on the dashboard's trend — and no keyframes at all. A
 * payroll screen has nothing to animate; figures appear when they are asked
 * for.
 *
 * So the honest half of this rule is not in this block. The movement in this
 * application is five `scrollIntoView({ behavior: 'smooth' })` calls in
 * app.js, and a stylesheet CANNOT reach them: an explicit `behavior` passed to
 * the call beats `scroll-behavior` in CSS, every time. `scrollToPanel` in
 * app.js asks the same question this block asks and passes `'auto'` when the
 * answer is yes, and `screen-affordances.test.ts` fails if a sixth smooth
 * scroll is ever written by hand instead of going through it.
 *
 * Nothing is hidden here and no layout moves. Somebody who has asked for less
 * movement has asked for less movement, not for a different application.
 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/**
 * ── On paper ──────────────────────────────────────────────────────────────
 *
 * The registers and the returns are read on paper, and until this block they
 * printed as the screen: the green bar across the top of every sheet, the
 * module buttons, the Aa, and every control beside a heading, each one an
 * instruction to somebody holding a page they cannot press.
 *
 * What this block is NOT for: payslips, the TD63 and the ΤΦ7 are documents the
 * server renders, and none of them comes through this file. This is for the
 * working screens — the register, the run, the Social Insurance return — when
 * somebody takes one to a meeting.
 *
 * Three decisions in it:
 *
 *   · the chrome goes, and so does every CONTROL. A printed button is a lie
 *     about what the paper can do. The `.panel` frame goes with them — a box
 *     drawn round a table wastes the margin a reader writes in.
 *   · the tables OPEN UP. On screen a row is separated by a hairline and the
 *     ground does the rest; on paper, where there is no hover and no tint, a
 *     hairline is all there is, so the rules are darkened and the header is
 *     repeated at the top of every sheet with `display: table-header-group`.
 *     A row is kept whole — a person's line broken across two pages is how a
 *     figure gets read against the wrong name.
 *   · nothing is RESIZED. The text size and the dark palette both live inside
 *     `@media screen` and never reach paper, which is the arrangement the zoom
 *     block near the top of this file sets out. This block does not undo them
 *     because there is nothing to undo.
 */
@media print {
  /* The chrome. The connection banner is here rather than in a print block of
     its own: this file has ONE, and `screen-affordances.test.ts` fails on a
     second — a stylesheet with two print blocks is one where the rules for
     paper disagree with each other somewhere nobody looks. (NET-1) */
  .shell-top, .shell-side, .shell-foot, .shell-scrim, .skip-link, #signin, #connection-lost { display: none !important; }
  .shell { display: block; min-height: 0; }

  /* Controls: nothing that has to be pressed. Links keep their text — a link
     in a table cell is usually a name, and dropping it would drop the name. */
  button, .btn, input[type="button"], input[type="submit"],
  .text-size, .theme-btn, .head-actions { display: none !important; }

  /* The page itself: ink on white, no frame, and the margins the printer gives
     us rather than the ones the screen wanted. */
  html, body { background: #fff !important; color: #000 !important; }
  main { padding: 0 !important; }
  .panel {
    border: 0 !important;
    border-radius: 0 !important;
    padding: 0 !important;
    max-width: none !important;
    background: transparent !important;
  }

  /* The heading carries the page, so it is worth the sheet it is on. */
  header.top { border-bottom: 1px solid #000; margin-bottom: 12px; }

  /* The tables. */
  table.grid { font-size: 11px; }
  table.grid thead { display: table-header-group; }
  table.grid th { border-bottom: 1px solid #000 !important; color: #000 !important; }
  table.grid td { border-bottom: 1px solid #999 !important; }
  table.grid tbody tr { page-break-inside: avoid; break-inside: avoid; cursor: auto !important; }
  table.grid tbody tr:hover { background: transparent !important; }
}

/**
 * The TD59's B9 row: the sum insured, and the sentence that says whether the
 * form's 7% test could be made at all.
 *
 * `.td59-untested` is deliberately not the red of an error. Nothing is wrong —
 * a premium with no sum insured behind it is an ordinary state of a
 * declaration, and the figure IS allowed. What it must not do is read like a
 * checked one, so it takes the amber that means "wants attention" everywhere
 * else on these screens. (C9-2)
 */
.td59-sum-insured { display: block; margin-top: 6px; }
.td59-sum-insured label {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--ink-soft);
}
.td59-sum-insured label input { width: 130px; }
.td59-sum-insured small { display: block; margin-top: 3px; font-size: 11px; color: var(--ink-soft); }
.td59-sum-insured small.td59-untested { color: var(--warn); font-weight: 600; }

/* SANDBOX-TOOL start · TEMPORARY · remove before launch · docs/SANDBOX-TOOL.md
   The assessment sandbox. Palette variables only — no colour is mixed here, so
   the screen answers in both themes without a dark rule of its own. */
/* Its own width. `.panel.wide` is 940px, which split in two gave a form column
   of about 300px — a questionnaire with dates, money and a timeline in it does
   not fit there, and the first thing anybody said about this screen was that it
   was confusing. */
#view-sandbox .panel { max-width: 1400px; }
.sandbox-layout { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: 22px; align-items: start; }
/* The answer stays in view while the history is being typed: it is the thing
   being watched, and scrolling away from it to change a date defeats the tool. */
.sandbox-result { position: sticky; top: 12px; }
@container app (max-width: 1050px) { .sandbox-layout { grid-template-columns: minmax(0, 1fr); } .sandbox-result { position: static; } }

.sandbox-intro { font-size: 13px; max-width: 95ch; margin: 10px 0 0; }
.sandbox-examples { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 18px; }
.sandbox-preset { display: block; width: 100%; text-align: left; font-size: 12px; padding: 7px 10px; }
.sandbox-preset strong { display: block; }
.sandbox-preset .muted { display: block; margin-top: 2px; font-weight: 400; }
#sandbox-paste-window h4 { font-size: 13px; margin: 0 0 4px; }
#sandbox-paste-window .muted { font-size: 12px; }

.sandbox-years { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 6px; }
.sandbox-year { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; white-space: nowrap; }
.sandbox-year input { margin: 0; }
.sandbox-timeline-empty { font-size: 12px; margin: 4px 0 8px; }

.sandbox-form { display: flex; flex-direction: column; gap: 14px; }
.sandbox-pane { border: 1px solid var(--line); border-radius: 8px; padding: 4px 14px 14px; background: var(--tint-soft); }
.sandbox-pane h3 { font-size: 13px; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-soft); margin: 12px 0 6px; }
.sandbox-pane > p.muted { margin: 0 0 8px; font-size: 12px; }

.sandbox-timeline { margin: 6px 0 14px; }
/* Each field labels itself and the row wraps: no header to line columns up
   against, because a Cyprus row and an overseas one do not have the same ones. */
.sandbox-timeline-row { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: flex-end; padding: 9px; border: 1px solid var(--line); border-radius: 6px; margin-bottom: 8px; background: var(--panel); }
.sandbox-timeline-row .sandbox-cell { display: flex; flex-direction: column; gap: 3px; }
.sandbox-timeline-row input, .sandbox-timeline-row select { max-width: 170px; }
.sandbox-timeline-row .sandbox-drop { align-self: flex-end; margin-left: auto; }


.sandbox-lead { border-left-width: 4px; }
.sandbox-amount { font-size: 20px; font-weight: 700; font-family: var(--sans); margin-top: 6px !important; }
.sandbox-final-eligible { color: var(--good); }
.sandbox-final-review { color: var(--warn); }
.sandbox-final-not { color: var(--bad); }
.sandbox-final-incomplete { color: var(--ink-soft); }
.sandbox-workings { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 10px; }
.sandbox-workings summary { cursor: pointer; font-size: 13px; color: var(--accent-text); }
.sandbox-workings[open] summary { margin-bottom: 10px; }
.sandbox-q { padding: 8px 0; border-top: 1px solid var(--line); }
.sandbox-q-label { display: block; font-size: 13px; }
.sandbox-q-label input, .sandbox-q-label select { display: block; margin-top: 5px; max-width: 320px; }
.sandbox-q-help { margin: 4px 0 0; font-size: 12px; }
.sandbox-q-source { margin: 2px 0 0; font-size: 11px; font-style: italic; }

.sandbox-rows { margin-top: 6px; }
.sandbox-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; padding: 8px; margin-bottom: 6px; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); }
.sandbox-cell { display: flex; flex-direction: column; gap: 3px; }
.sandbox-cell-label { font-size: 11px; }
.sandbox-cell input, .sandbox-cell select { max-width: 190px; }

.sandbox-result { border: 1px solid var(--line); border-radius: 8px; padding: 14px; background: var(--panel); }
.sandbox-result h3 { font-size: 13px; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-soft); margin: 16px 0 8px; }
.sandbox-verdict { padding-bottom: 8px; border-bottom: 1px solid var(--line); }
.sandbox-verdict p { margin: 6px 0 0; }
.sandbox-final { display: inline-block; font-weight: 700; letter-spacing: .04em; }

.sandbox-route { border: 1px solid var(--line); border-left: 3px solid var(--line-strong); border-radius: 6px; padding: 8px 10px; margin-bottom: 8px; }
.sandbox-route-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.sandbox-route p { margin: 4px 0 0; font-size: 12px; }
.sandbox-badge { font-size: 11px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; }
.sandbox-reasons { margin: 6px 0 0; padding-left: 18px; font-size: 12px; }
.sandbox-reasons li { margin-bottom: 3px; }
.sandbox-missing { font-size: 12px; color: var(--warn); }

.sandbox-eligible { border-left-color: var(--good); }
.sandbox-eligible .sandbox-badge { color: var(--good); }
.sandbox-review, .sandbox-incomplete { border-left-color: var(--warn); }
.sandbox-review .sandbox-badge, .sandbox-incomplete .sandbox-badge { color: var(--warn); }
.sandbox-not-eligible, .sandbox-not-applicable, .sandbox-qualifies-not-this-year { border-left-color: var(--line-strong); }
.sandbox-not-eligible .sandbox-badge, .sandbox-not-applicable .sandbox-badge, .sandbox-qualifies-not-this-year .sandbox-badge { color: var(--ink-soft); }

.sandbox-flags ul { margin: 0; padding-left: 18px; font-size: 12px; }
.sandbox-flag-warning { color: var(--warn); }
#sandbox-paste-text { width: 100%; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; }
/* SANDBOX-TOOL end */

/* Where you are signed in (LOW-1): the address under the browser, smaller, and
   the button on one line however narrow the column. */
#sessions-table .session-address { font-size: 0.85em; font-variant-numeric: tabular-nums; }
#sessions-table .session-end { white-space: nowrap; }

/* Keeping and erasing, on a person's screen (H4-1): the boxes one under another. */
.erase-form { display: grid; gap: 8px; max-width: 420px; margin-top: 12px; }
.erase-form label { font-size: 13px; color: var(--ink-soft); }
.erase-form button { justify-self: start; }
