/* Tradescom — the six looks. Lane wT, 2026-09-04.
 *
 * WHAT THIS IS. Zach was shown five design directions as drawings
 * (`build/design/direction-{1..5}.html`, documented in `build/design/README.md`)
 * and said "1 and 5 are my favorite", then "give people the options". So the
 * looks are not a drawing and not a company decision: every person picks their
 * own, on their own account, and it follows them from the Mac to the phone.
 *
 * HOW IT WORKS. `app.css` defines every colour, radius and size as a custom
 * property on `:root`. This file redefines those same properties under
 * `:root[data-look="…"]`. An attribute selector on the root element is
 * specificity (0,2,0) against app.css's plain `:root` (0,1,0), so a look wins
 * over BOTH the dark defaults and the `prefers-color-scheme: light` block
 * without app.css being restructured and without a single `!important`.
 * `src/look.js` sets that one attribute.
 *
 * NOTHING HERE RUNS BY DEFAULT. With no `data-look` attribute the app is
 * byte-for-byte the app it is today. A person who never opens the picker sees
 * no change at all.
 *
 * THE RULES EVERY LOOK KEEPS, because they are not decoration:
 *   1. Nothing hidden — no look shrinks a screen's actions out of view or
 *      behind a menu. Density changes; the inventory of what is on the screen
 *      does not.
 *   2. The stage buttons are the biggest thing on a job. Every look sizes
 *      `.stage-btn.is-open` at or above what app.css gives it.
 *   3. One designated row per tech on the board. `.sched-lane` stays a lane in
 *      all six; a look repaints it, it never merges two people into one row.
 *   4. 44px tap floor (`--tap-min`) is never touched by a look. Ever.
 *   5. Every text/background pair below clears WCAG AA 4.5:1, checked with
 *      `scripts/_contrast.py`. Numbers are in the header of each block.
 *   6. The company mark is the tenant's, not the person's: no look restyles
 *      the logo. L&P's navy plumber stays navy under all six.
 *
 * FONTS — the one honest compromise. The directions pair real families (Inter
 * Tight, Public Sans + IBM Plex Mono, Figtree, Source Serif 4, Manrope). This
 * app's CSP is `default-src 'self'; font-src 'self'` and web/_headers says out
 * loud that it loads nothing from a third party, so a Google Fonts <link> is
 * not available and self-hosting five families is a much bigger change than a
 * look picker should make. Each look therefore uses the closest thing the
 * platform already has — and where the type IS the idea (Board's mono digits,
 * Ledger's serif name) that is done with a real generic family, not a
 * substitute nobody has. Noted in the report; a later pass can self-host.
 */

/* ==========================================================================
 * 1 — PAPER.  White, hairlines, one navy. Nothing is a card.
 *
 * Accent #1F2E45 is sampled off the L&P mark, so the app and the truck wrap
 * are the same navy. Contrast: ink 18.8, ink-2 5.8, ink-dim 4.9/4.6, accent
 * 13.7, white-on-navy 13.7, every tone ink >= 4.8 on its own tint.
 * ========================================================================== */

:root[data-look='paper'] {
  --bg: #ffffff;
  --card: #ffffff;
  --card-2: #f7f8f9;
  --surface-alt: #f7f8f9;
  --line: #e8eaed;
  --line-strong: #d3d8de;
  --fill: rgba(31, 46, 69, 0.07);
  --scrim: rgba(15, 18, 22, 0.34);

  --ink: #0f1216;
  --ink-2: #5c6672;
  --ink-dim: #626b79;
  --ink-on-fill: #ffffff;

  --accent: #1f2e45;
  --accent-deep: #16223a;
  --accent-soft: rgba(31, 46, 69, 0.09);
  --focus-ring: #3a5480;

  --fill-accent: #1f2e45;
  --fill-done: #157a3c;
  --fill-soon: #8a5200;
  --fill-late: #be2a1c;

  --tone-done-bg: #eaf6ee;
  --tone-done-ink: #157a3c;
  --tone-done-edge: #1a8c46;
  --tone-live-bg: #e9f1fc;
  --tone-live-ink: #0a5fbf;
  --tone-live-edge: #0a5fbf;
  --tone-soon-bg: #fbf2e3;
  --tone-soon-ink: #9a5b00;
  --tone-soon-edge: #b06a00;
  --tone-late-bg: #fdf0ef;
  --tone-late-ink: #be2a1c;
  --tone-late-edge: #be2a1c;
  --tone-idle-bg: #f1f3f5;
  --tone-idle-ink: #4a5560;
  --tone-idle-edge: #a8b0b8;
  --tone-plan-bg: #f1eefb;
  --tone-plan-ink: #5b45b8;
  --tone-plan-edge: #5b45b8;

  --money-out: #5c6672;

  /* No shadows at all. The hairline carries the edge — that is the whole bet. */
  --elev-1: none;
  --elev-2: none;
  --elev-3: 0 10px 28px rgba(15, 18, 22, 0.14);

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --radius-xl: 20px;

  --text-body: 17px;
  --tap-primary: 54px;

  /* app.css's bottom bar asks for two names nothing defines and falls
   * back to dark literals, which is why the nav read at 3.1:1 on white.
   * Defining them here fixes it without a rule and without touching
   * app.css. See app.css:1959 and :1962. */
  --muted: #5c6672;
  --chip-on: #eef1f5;

  --attention-soft: #fdf3e3; /* amber note stays readable: root default is the dark one */
  --bad-soft: #fdecea;
  color-scheme: light;
}

/* --- Paper's structure: rows, not boxes ---------------------------------- */

/* A list stops being a stack of cards and becomes a printed work order: rows
 * separated by one hairline, full width, no gaps to break the column. */
:root[data-look='paper'] .ts-stack {
  gap: 0;
}
:root[data-look='paper'] .ts-row {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line);
  border-left: 3px solid var(--tone-idle-edge);
  border-radius: 0;
  min-height: 66px;
  padding: var(--space-3) var(--space-4);
}
:root[data-look='paper'] .ts-stack > .ts-row:first-child,
:root[data-look='paper'] .stops > .ts-row:first-child {
  border-top: 1px solid var(--line);
}
:root[data-look='paper'] .ts-row:active {
  background: var(--card-2);
  transform: none;
}
/* The group and the card lose their container; the rows inside keep the rules. */
:root[data-look='paper'] .ts-group,
:root[data-look='paper'] .ts-card,
:root[data-look='paper'] .card {
  background: transparent;
  border-left: 0;
  border-right: 0;
  border-radius: 0;
  box-shadow: none;
}
:root[data-look='paper'] .card {
  border-top: 1px solid var(--line);
  border-bottom: 0;
  padding-left: 0;
  padding-right: 0;
}
:root[data-look='paper'] .ts-row-title {
  font-size: 17px;
  font-weight: var(--w-semi);
  letter-spacing: -0.005em;
}
/* One navy button, and it is the only fill on the screen. */
:root[data-look='paper'] .ts-btn {
  background: #ffffff;
  border-color: var(--line-strong);
}
:root[data-look='paper'] .stop,
:root[data-look='paper'] .sched-stop {
  box-shadow: none;
}

/* ==========================================================================
 * 2 — BOARD.  The dispatcher's look. Mono digits, flat panels, tight rows.
 *
 * Contrast on #EEF0F4: ink 15.7, ink-2 5.4, ink-dim 4.8, tones 5.4–6.6,
 * white on #1256E0 6.1.
 * ========================================================================== */

:root[data-look='board'] {
  /* The one place a look changes type: times, money and PO numbers set in a
   * real monospace so a column of them lines up and digits never jitter.
   * `ui-monospace` is the platform's own; the fallbacks cover the rest. */
  --font-num: ui-monospace, SFMono-Regular, 'SF Mono', 'Cascadia Mono', 'Roboto Mono',
    Menlo, Consolas, monospace;

  --bg: #eef0f4;
  --card: #ffffff;
  --card-2: #f7f8fa;
  --surface-alt: #f7f8fa;
  --line: #dce0e7;
  --line-strong: #c6ccd6;
  --fill: rgba(17, 23, 34, 0.06);
  --scrim: rgba(17, 23, 34, 0.36);

  --ink: #111722;
  --ink-2: #586274;
  --ink-dim: #626a7b;
  --ink-on-fill: #ffffff;

  --accent: #1256e0;
  --accent-deep: #0e44b0;
  --accent-soft: #edf3ff;
  --focus-ring: #1256e0;

  --fill-accent: #1256e0;
  --fill-done: #146b33;
  --fill-soon: #8f5400;
  --fill-late: #b92a1d;

  --tone-done-bg: #e4f3e9;
  --tone-done-ink: #146b33;
  --tone-done-edge: #1a8c46;
  --tone-live-bg: #e6efff;
  --tone-live-ink: #0b57bf;
  --tone-live-edge: #1256e0;
  --tone-soon-bg: #faf0dd;
  --tone-soon-ink: #8f5400;
  --tone-soon-edge: #b06a00;
  --tone-late-bg: #fbe9e7;
  --tone-late-ink: #b92a1d;
  --tone-late-edge: #d33121;
  --tone-idle-bg: #e6e9ef;
  --tone-idle-ink: #4b5563;
  --tone-idle-edge: #98a1af;
  --tone-plan-bg: #eeeafb;
  --tone-plan-ink: #5a3fb0;
  --tone-plan-edge: #6c4fd0;

  --money-out: #586274;

  --elev-1: none;
  --elev-2: none;
  --elev-3: 0 8px 24px rgba(17, 23, 34, 0.16);

  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 6px;
  --radius-xl: 12px;

  --text-body: 16px;
  --tap-primary: 52px;

  /* app.css's bottom bar asks for two names nothing defines and falls
   * back to dark literals, which is why the nav read at 3.1:1 on white.
   * Defining them here fixes it without a rule and without touching
   * app.css. See app.css:1959 and :1962. */
  --muted: #586274;
  --chip-on: #e6e9ef;

  --attention-soft: #fdf3e3; /* amber note stays readable: root default is the dark one */
  --bad-soft: #fdecea;
  color-scheme: light;
}

/* Flat panels on a cool ground, and the rows pulled in so a whole day fits. */
:root[data-look='board'] .ts-card,
:root[data-look='board'] .ts-group,
:root[data-look='board'] .ts-row,
:root[data-look='board'] .card {
  box-shadow: none;
}
:root[data-look='board'] .ts-stack {
  gap: var(--space-2);
}
:root[data-look='board'] .ts-row {
  min-height: 56px;
  padding: var(--space-2) var(--space-3);
}
:root[data-look='board'] .ts-row-title {
  font-size: 17px;
}
/* Every digit on the screen in the mono face — this is the look's whole idea. */
:root[data-look='board'] .ts-row-time,
:root[data-look='board'] .ts-money,
:root[data-look='board'] .ts-day-num,
:root[data-look='board'] .sched-tally-n b,
:root[data-look='board'] .time,
:root[data-look='board'] .mb-id,
:root[data-look='board'] .mb-date,
:root[data-look='board'] .mb-amt-main,
:root[data-look='board'] .mb-hero-amount,
:root[data-look='board'] .ts-tag,
:root[data-look='board'] .sched-lane-count {
  font-family: var(--font-num);
  letter-spacing: -0.02em;
}
/* A lane heading reads as a label on a timeline, not as a name in a list. */
:root[data-look='board'] .sched-lane-head {
  background: var(--card-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-2);
  min-height: 34px;
}

/* ==========================================================================
 * 3 — TRUCK.  Drawn for the man in the cab. 18px body, 62px primary, warm
 * off-white that does not glare, and the only look that ships a dark palette.
 *
 * Light contrast: ink 16.1, ink-2 5.5, ink-dim 4.8, accent 5.4, white on
 * #00706B 6.0. Dark: ink 16.6, ink-2 7.3, ink-dim 5.4, accent 7.3,
 * white on #1E7C74 5.0.
 * ========================================================================== */

:root[data-look='truck'] {
  --bg: #f7f4ef;
  --card: #ffffff;
  --card-2: #fbf9f6;
  --surface-alt: #fbf9f6;
  --line: #e8e1d6;
  --line-strong: #d8cfc0;
  --fill: rgba(23, 25, 26, 0.06);
  --scrim: rgba(23, 25, 26, 0.36);

  --ink: #17191a;
  --ink-2: #5e645f;
  --ink-dim: #676d69;
  --ink-on-fill: #ffffff;

  --accent: #00706b;
  --accent-deep: #005853;
  --accent-soft: #e1f0ee;
  --focus-ring: #00706b;

  --fill-accent: #00706b;
  --fill-done: #14713a;
  --fill-soon: #8c5300;
  --fill-late: #bb2a1c;

  --tone-done-bg: #e6f2e9;
  --tone-done-ink: #14713a;
  --tone-done-edge: #1d8a49;
  --tone-live-bg: #e1f0ee;
  --tone-live-ink: #005853;
  --tone-live-edge: #00706b;
  --tone-soon-bg: #f8efdf;
  --tone-soon-ink: #8c5300;
  --tone-soon-edge: #ad6a00;
  --tone-late-bg: #fbeae7;
  --tone-late-ink: #bb2a1c;
  --tone-late-edge: #d13424;
  --tone-idle-bg: #eeeae3;
  --tone-idle-ink: #4d534e;
  --tone-idle-edge: #a09a90;
  --tone-plan-bg: #ede9f7;
  --tone-plan-ink: #58419f;
  --tone-plan-edge: #6d55bd;

  --money-out: #5e645f;

  --elev-1: 0 1px 2px rgba(40, 33, 22, 0.06);
  --elev-2: 0 2px 6px rgba(40, 33, 22, 0.09);
  --elev-3: 0 10px 26px rgba(40, 33, 22, 0.18);

  --radius-sm: 10px;
  --radius-md: 14px;
  --radius-lg: 18px;
  --radius-xl: 24px;

  /* The two numbers that make this the field look. Nothing tappable under 60. */
  --text-body: 18px;
  --text-label: 16px;
  --tap-comfortable: 60px;
  --tap-primary: 62px;

  /* app.css's bottom bar asks for two names nothing defines and falls
   * back to dark literals, which is why the nav read at 3.1:1 on white.
   * Defining them here fixes it without a rule and without touching
   * app.css. See app.css:1959 and :1962. */
  --muted: #5e645f;
  --chip-on: #eeeae3;

  --attention-soft: #fdf3e3; /* amber note stays readable: root default is the dark one */
  --bad-soft: #fdecea;
  color-scheme: light;
}

/* Truck is the only direction that ships both palettes, so it is the only one
 * that needs a dark half. Same specificity as the block above, later in the
 * file, so it wins when the phone is set to dark — which is where this look
 * spends its evenings. */
@media (prefers-color-scheme: dark) {
  :root[data-look='truck'] {
    --bg: #111413;
    --card: #1b1f1e;
    --card-2: #262a29;
    --surface-alt: #262a29;
    --line: #2e3331;
    --line-strong: #3d4341;
    --fill: rgba(243, 242, 239, 0.09);
    --scrim: rgba(0, 0, 0, 0.58);

    --ink: #f3f2ef;
    --ink-2: #a6ada8;
    --ink-dim: #8d948f;
    --ink-on-fill: #ffffff;

    --accent: #35bfb0;
    --accent-deep: #2c8f86;
    --accent-soft: #12312e;
    --focus-ring: #35bfb0;

    --fill-accent: #1e7c74;
    --fill-done: #24883d;
    --fill-soon: #ad6500;
    --fill-late: #d13324;

    --tone-done-bg: #122a1c;
    --tone-done-ink: #6ee79b;
    --tone-done-edge: #2fae5c;
    --tone-live-bg: #12312e;
    --tone-live-ink: #5fd8c8;
    --tone-live-edge: #35bfb0;
    --tone-soon-bg: #2d2109;
    --tone-soon-ink: #f5c15a;
    --tone-soon-edge: #c98a12;
    --tone-late-bg: #331916;
    --tone-late-ink: #ff9d8f;
    --tone-late-edge: #e05244;
    --tone-idle-bg: #262a29;
    --tone-idle-ink: #b3b9b4;
    --tone-idle-edge: #6e746f;
    --tone-plan-bg: #241f38;
    --tone-plan-ink: #c3aef7;
    --tone-plan-edge: #8b70e0;

    --money-out: #9aa39c;

    --elev-1: 0 1px 2px rgba(0, 0, 0, 0.3);
    --elev-2: 0 2px 8px rgba(0, 0, 0, 0.36);
    --elev-3: 0 10px 26px rgba(0, 0, 0, 0.5);

    --attention-soft: #2a2113;
    --bad-soft: #2e1917;
    color-scheme: dark;
  }
}

@media (prefers-color-scheme: dark) {
  :root[data-look='truck'] .chip.on,
  :root[data-look='truck'] .primary,
  :root[data-look='truck'] .actions .button.primary,
  :root[data-look='truck'] .ts-day.is-on {
    background: var(--fill-accent);
    border-color: var(--fill-accent);
    color: var(--ink-on-fill);
  }
  :root[data-look='truck'] {
    --muted: #a6ada8;
    --chip-on: #262a29;
  }
}

/* Big targets, generous rows. The whole point of this one. */
:root[data-look='truck'] .ts-row {
  min-height: 72px;
  padding: var(--space-4);
}
:root[data-look='truck'] .ts-row-title {
  font-size: 20px;
}
:root[data-look='truck'] .ts-btn,
:root[data-look='truck'] .ts-btn-danger {
  min-height: 60px;
}
:root[data-look='truck'] .ts-btn-quiet {
  min-height: 52px;
}
:root[data-look='truck'] .stage-btn {
  min-height: 66px;
}
:root[data-look='truck'] .stage-btn.is-open {
  min-height: 78px;
}
:root[data-look='truck'] .chip,
:root[data-look='truck'] .ts-day {
  min-height: 52px;
}

/* ==========================================================================
 * 4 — LEDGER.  Black on white. Colour means one thing only: status.
 *
 * A serif for the name of the human, a grotesque for everything you act on.
 * `ui-serif` is the platform's own reading serif (New York / Georgia), which
 * is the closest a self-hosted-nothing app gets to Source Serif 4.
 * Contrast: ink 17.8, ink-2 7.2, ink-dim 5.1, white on black 17.8.
 * ========================================================================== */

:root[data-look='ledger'] {
  --font-serif: ui-serif, 'New York', 'Source Serif 4', Georgia, 'Times New Roman', serif;

  --bg: #ffffff;
  --card: #ffffff;
  --card-2: #faf9f7;
  --surface-alt: #faf9f7;
  --line: #e4e2de;
  --line-strong: #c9c7c2;
  --fill: rgba(23, 24, 26, 0.06);
  --scrim: rgba(23, 24, 26, 0.34);

  --ink: #17181a;
  --ink-2: #55585c;
  --ink-dim: #65686c;
  --ink-on-fill: #ffffff;

  --accent: #17181a;
  --accent-deep: #000000;
  --accent-soft: #f0efed;
  --focus-ring: #17181a;

  --fill-accent: #17181a;
  --fill-done: #14713a;
  --fill-soon: #8a5200;
  --fill-late: #b8291b;

  /* The six tones are the ONLY colour on this screen, which is the whole point
   * of it, so they are left saturated and the interface around them is not. */
  --tone-done-bg: #e7f4ec;
  --tone-done-ink: #14713a;
  --tone-done-edge: #16a34a;
  --tone-live-bg: #e5eefc;
  --tone-live-ink: #1146ad;
  --tone-live-edge: #2563eb;
  --tone-soon-bg: #fbf1de;
  --tone-soon-ink: #8a5200;
  --tone-soon-edge: #d97706;
  --tone-late-bg: #fceae8;
  --tone-late-ink: #b8291b;
  --tone-late-edge: #dc2626;
  --tone-idle-bg: #f0efed;
  --tone-idle-ink: #4b5563;
  --tone-idle-edge: #9ca3af;
  --tone-plan-bg: #efe9fa;
  --tone-plan-ink: #5b21b6;
  --tone-plan-edge: #8b5cf6;

  --money-out: #55585c;

  --elev-1: none;
  --elev-2: none;
  --elev-3: 0 10px 26px rgba(23, 24, 26, 0.14);

  --radius-sm: 3px;
  --radius-md: 4px;
  --radius-lg: 4px;
  --radius-xl: 10px;

  --text-body: 16px;
  --tap-primary: 54px;

  /* app.css's bottom bar asks for two names nothing defines and falls
   * back to dark literals, which is why the nav read at 3.1:1 on white.
   * Defining them here fixes it without a rule and without touching
   * app.css. See app.css:1959 and :1962. */
  --muted: #55585c;
  --chip-on: #f0efed;

  --attention-soft: #fdf3e3; /* amber note stays readable: root default is the dark one */
  --bad-soft: #fdecea;
  color-scheme: light;
}

/* Rules instead of boxes, columns instead of cards. */
:root[data-look='ledger'] .ts-stack {
  gap: 0;
}
:root[data-look='ledger'] .ts-row {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line);
  border-left: 3px solid var(--tone-idle-edge);
  border-radius: 0;
  padding: var(--space-3) var(--space-4);
}
:root[data-look='ledger'] .ts-row:active {
  background: var(--card-2);
  transform: none;
}
:root[data-look='ledger'] .ts-card,
:root[data-look='ledger'] .ts-group,
:root[data-look='ledger'] .card {
  border-left: 0;
  border-right: 0;
  border-radius: 0;
  box-shadow: none;
}
/* Names use the same sans as the rest of the row. A serif on some titles and
 * not others made one list look like two apps. */
:root[data-look='ledger'] .ts-row-title,
:root[data-look='ledger'] .job-who,
:root[data-look='ledger'] .mb-cust,
:root[data-look='ledger'] .sched-lane-name {
  font-family: var(--font-ui);
  font-weight: var(--w-semi);
  letter-spacing: 0;
}
/* Money is a right-aligned column, which is what `.ts-row-side` already is. */
:root[data-look='ledger'] .ts-money,
:root[data-look='ledger'] .mb-amt-main {
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* ==========================================================================
 * 5 — FOCUS.  Ranked, not hidden. Soft tinted blocks, generous radii, indigo.
 *
 * Contrast on #F6F6FA: ink 18.4, ink-2 6.2, ink-dim 4.8, tones 5.0–5.4,
 * white on #4F46E5 6.3, #3A32C0 on the tint 7.7.
 * ========================================================================== */

:root[data-look='focus'] {
  --bg: #ffffff;
  --card: #f6f6fa;
  --card-2: #f0effe;
  --surface-alt: #f0effe;
  --line: #ededf3;
  --line-strong: #dedee8;
  --fill: rgba(79, 70, 229, 0.07);
  --scrim: rgba(20, 20, 26, 0.34);

  --ink: #14141a;
  --ink-2: #5b5b6a;
  --ink-dim: #6b6b80;
  --ink-on-fill: #ffffff;

  --accent: #4f46e5;
  --accent-deep: #3a32c0;
  --accent-soft: #f0effe;
  --focus-ring: #4f46e5;

  --fill-accent: #4f46e5;
  --fill-done: #17794a;
  --fill-soon: #8f5400;
  --fill-late: #c22b21;

  --tone-done-bg: #e8f5ee;
  --tone-done-ink: #17794a;
  --tone-done-edge: #1d9159;
  --tone-live-bg: #e9f5f7;
  --tone-live-ink: #0e7490;
  --tone-live-edge: #0e7490;
  --tone-soon-bg: #fcf4e9;
  --tone-soon-ink: #8f5400;
  --tone-soon-edge: #b06a00;
  --tone-late-bg: #fdf0ef;
  --tone-late-ink: #c22b21;
  --tone-late-edge: #c22b21;
  --tone-idle-bg: #f0f0f5;
  --tone-idle-ink: #4f4f60;
  --tone-idle-edge: #b8b8c4;
  /* Direction 5 moves the estimate tone to magenta on purpose: at indigo it
   * read as the accent, so a quote looked like the app's own chrome. */
  --tone-plan-bg: #faeef6;
  --tone-plan-ink: #ae2f8c;
  --tone-plan-edge: #ae2f8c;

  --money-out: #5b5b6a;

  /* No borders, no shadows — space and tint do the separating. */
  --elev-1: none;
  --elev-2: none;
  --elev-3: 0 12px 32px rgba(20, 20, 26, 0.14);

  --radius-sm: 11px;
  --radius-md: 14px;
  --radius-lg: 20px;
  --radius-xl: 26px;

  --text-body: 16px;
  --tap-primary: 56px;

  /* app.css's bottom bar asks for two names nothing defines and falls
   * back to dark literals, which is why the nav read at 3.1:1 on white.
   * Defining them here fixes it without a rule and without touching
   * app.css. See app.css:1959 and :1962. */
  --muted: #5b5b6a;
  --chip-on: #f0effe;

  --attention-soft: #fdf3e3; /* amber note stays readable: root default is the dark one */
  --bad-soft: #fdecea;
  color-scheme: light;
}

/* A quiet list on tinted blocks, separated by space rather than lines. */
:root[data-look='focus'] .ts-stack {
  gap: 6px;
}
:root[data-look='focus'] .ts-row {
  background: var(--card);
  border: 0;
  border-left: 4px solid var(--tone-idle-edge);
  border-radius: 16px;
  min-height: 74px;
  padding: var(--space-3) var(--space-4);
}
:root[data-look='focus'] .ts-card,
:root[data-look='focus'] .ts-group,
:root[data-look='focus'] .card {
  background: var(--card);
  border-color: transparent;
  box-shadow: none;
}
:root[data-look='focus'] .ts-group > * + * {
  border-top-color: #ffffff;
}
/* Ranking is done by SIZE, so no label has to say "this one". The current
 * stage is the biggest button in the app under this look. */
:root[data-look='focus'] .job-stage-bar {
  background: var(--accent-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
}
:root[data-look='focus'] .stage-btn.is-open {
  min-height: 76px;
  font-size: var(--text-heading);
}
/* The hero: one tinted block that answers "what now" in the top third. */
:root[data-look='focus'] .ts-hero,
:root[data-look='focus'] .mb-hero,
:root[data-look='focus'] .hm-needsme {
  background: var(--accent-soft);
  border-color: transparent;
  border-radius: var(--radius-lg);
}

/* ==========================================================================
 * 6 — PAPER FOCUS.  Paper's structure and restraint carrying Focus's one real
 * idea: the single thing you must deal with right now is a tinted block at the
 * top of the screen, and everything else is a quiet hairline row.
 *
 * Deliberately NOT a blend of the palettes. It takes Paper's navy, Paper's
 * white, Paper's hairlines and Paper's radii whole, and takes from Focus only
 * the ranking — no indigo, no card-everywhere density.
 *
 * The hero is not guessed at: it is whatever the app has ALREADY decided is
 * the thing that needs a person now — "Needs me" on Home, the work with no
 * truck on the board, the current stage on a job, and any row the app has
 * tagged live or late, which is the unanswered thread in Messages and the
 * stop that is running now on the board.
 * ========================================================================== */

:root[data-look='paperfocus'] {
  --bg: #ffffff;
  --card: #ffffff;
  --card-2: #f7f8f9;
  --surface-alt: #f7f8f9;
  --line: #e8eaed;
  --line-strong: #d3d8de;
  --fill: rgba(31, 46, 69, 0.07);
  --scrim: rgba(15, 18, 22, 0.34);

  --ink: #0f1216;
  --ink-2: #5c6672;
  --ink-dim: #626b79;
  --ink-on-fill: #ffffff;

  --accent: #1f2e45;
  --accent-deep: #16223a;
  --accent-soft: #eef1f5;
  --focus-ring: #3a5480;

  --fill-accent: #1f2e45;
  --fill-done: #157a3c;
  --fill-soon: #8a5200;
  --fill-late: #be2a1c;

  --tone-done-bg: #eaf6ee;
  --tone-done-ink: #157a3c;
  --tone-done-edge: #1a8c46;
  --tone-live-bg: #e9f1fc;
  --tone-live-ink: #0a5fbf;
  --tone-live-edge: #0a5fbf;
  --tone-soon-bg: #fbf2e3;
  --tone-soon-ink: #9a5b00;
  --tone-soon-edge: #b06a00;
  --tone-late-bg: #fdf0ef;
  --tone-late-ink: #be2a1c;
  --tone-late-edge: #be2a1c;
  --tone-idle-bg: #f1f3f5;
  --tone-idle-ink: #4a5560;
  --tone-idle-edge: #a8b0b8;
  --tone-plan-bg: #f1eefb;
  --tone-plan-ink: #5b45b8;
  --tone-plan-edge: #5b45b8;

  --money-out: #5c6672;

  --elev-1: none;
  --elev-2: none;
  --elev-3: 0 10px 28px rgba(15, 18, 22, 0.14);

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --radius-xl: 20px;

  --text-body: 17px;
  --tap-primary: 54px;

  /* app.css's bottom bar asks for two names nothing defines and falls
   * back to dark literals, which is why the nav read at 3.1:1 on white.
   * Defining them here fixes it without a rule and without touching
   * app.css. See app.css:1959 and :1962. */
  --muted: #5c6672;
  --chip-on: #eef1f5;

  --attention-soft: #fdf3e3; /* amber note stays readable: root default is the dark one */
  --bad-soft: #fdecea;
  color-scheme: light;
}

/* --- Paper's half: hairline rows, no containers -------------------------- */

:root[data-look='paperfocus'] .ts-stack {
  gap: 0;
}
:root[data-look='paperfocus'] .ts-row {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line);
  border-left: 3px solid var(--tone-idle-edge);
  border-radius: 0;
  min-height: 66px;
  padding: var(--space-3) var(--space-4);
}
:root[data-look='paperfocus'] .ts-row:active {
  background: var(--card-2);
  transform: none;
}
:root[data-look='paperfocus'] .ts-group,
:root[data-look='paperfocus'] .ts-card,
:root[data-look='paperfocus'] .card {
  background: transparent;
  border-left: 0;
  border-right: 0;
  border-radius: 0;
  box-shadow: none;
}
:root[data-look='paperfocus'] .card {
  border-top: 1px solid var(--line);
  border-bottom: 0;
  padding-left: 0;
  padding-right: 0;
}
:root[data-look='paperfocus'] .ts-row-title {
  font-size: 17px;
  font-weight: var(--w-semi);
  letter-spacing: -0.005em;
}
:root[data-look='paperfocus'] .ts-btn {
  background: #ffffff;
  border-color: var(--line-strong);
}

/* --- Focus's half: exactly one idea, the tinted hero --------------------- */

/* HOME. `.hm-needsme` is the app's own answer to "what needs a person right
 * now" (home.js:517) and it is already the first block on the screen, so the
 * hero is a repaint, not a rearrangement. */
:root[data-look='paperfocus'] .hm-needsme {
  background: var(--card-2);
  border: 0;
  border-left: 4px solid var(--accent);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}
:root[data-look='paperfocus'] .hm-needsme h3 {
  color: var(--accent);
}
/* THE BOARD. Work nobody owns is the one thing on this screen that only the
 * dispatcher can fix, so it is the hero — app.css already tints it; here it
 * gets the hero's weight. One row per tech is untouched. */
:root[data-look='paperfocus'] .sched-lane.is-needs-crew .sched-lane-stops {
  border-left: 4px solid var(--tone-late-edge);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}
/* A JOB. The current stage is the thing to deal with, and it is already the
 * biggest button — this makes the block around it read as the hero too. */
:root[data-look='paperfocus'] .job-stage-bar {
  background: var(--card-2);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius-md);
}
:root[data-look='paperfocus'] .stage-btn.is-open {
  min-height: 64px;
}
/* EVERY LIST, including Messages. A row the app has tagged live (running now,
 * unanswered) or late (overdue, missed) stops being a hairline row and becomes
 * a tinted block. Nothing is hidden by this — the quiet rows are all still
 * there, in full, in the same order. They are just no longer competing. */
:root[data-look='paperfocus'] .ts-row[data-tone='live'],
:root[data-look='paperfocus'] .ts-row[data-tone='late'] {
  background: var(--tone-live-bg);
  border-left-width: 4px;
  border-radius: var(--radius-md);
  border-top-color: transparent;
}
:root[data-look='paperfocus'] .ts-row[data-tone='late'] {
  background: var(--tone-late-bg);
}
:root[data-look='paperfocus'] .ts-row[data-tone='live'] .ts-row-title,
:root[data-look='paperfocus'] .ts-row[data-tone='late'] .ts-row-title {
  font-size: var(--text-heading);
  font-weight: var(--w-bold);
}
:root[data-look='paperfocus'] .ts-hero,
:root[data-look='paperfocus'] .mb-hero {
  background: var(--card-2);
  border: 0;
  border-left: 4px solid var(--accent);
  border-radius: var(--radius-md);
}

/* ==========================================================================
 * The picker itself. Two places, per the "nothing hidden" law: a row in
 * Settings, and a compact strip on the board where a person is actually
 * staring at their own day when they decide they hate the look.
 *
 * Styled with the tokens, so the picker is drawn IN the look it is offering.
 * ========================================================================== */

.look-picker {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.look-picker-title {
  font-size: var(--text-caption);
  font-weight: var(--w-bold);
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  color: var(--ink-dim);
}
.look-opts {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.look-opt {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: var(--tap-comfortable);
  padding: var(--space-3) var(--space-4);
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.look-opt.is-on {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.look-opt-swatch {
  flex: 0 0 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.look-opt-swatch i {
  display: block;
  flex: 1;
}
.look-opt-body {
  flex: 1;
  min-width: 0;
}
.look-opt-name {
  display: block;
  font-size: var(--text-body);
  font-weight: var(--w-semi);
}
.look-opt-why {
  display: block;
  font-size: var(--text-caption);
  color: var(--ink-2);
  line-height: var(--lh-snug);
}
.look-opt-tick {
  flex: 0 0 auto;
  color: var(--accent);
  font-weight: var(--w-bold);
}
.look-state {
  font-size: var(--text-caption);
  color: var(--ink-dim);
  min-height: 18px;
}

/* The compact strip. One row, chips at the tap floor, horizontally scrollable
 * rather than shrunk — the same hard rule the week strip follows. */
.look-strip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-bottom: var(--space-2);
  overflow-x: auto;
  scrollbar-width: none;
}
.look-strip::-webkit-scrollbar {
  display: none;
}
.look-strip-label {
  flex: 0 0 auto;
  font-size: var(--text-micro);
  font-weight: var(--w-bold);
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  color: var(--ink-dim);
}
.look-chip {
  flex: 0 0 auto;
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  border-radius: var(--radius-full);
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink-2);
  font-size: var(--text-label);
  font-weight: var(--w-semi);
  cursor: pointer;
}
.look-chip.is-on {
  background: var(--fill-accent);
  border-color: var(--fill-accent);
  color: var(--ink-on-fill);
}

/* ==========================================================================
 * LAW 2, held under every look: the stage buttons are the biggest thing on a
 * job, and the CURRENT stage is the biggest of those.
 *
 * Measured on the running app rather than assumed, and the measurement found a
 * defect that is NOT a look's: in the app as it ships today the current stage
 * is 56px while "Read the call" and "Bill it and finish" — ordinary
 * `.row-button`s further down the same screen — are 63px. So the default app
 * fails its own second law. Focus (76px), Truck (78px) and Paper Focus (64px)
 * already cleared it by their own design; these three lines bring the other
 * three looks over the same bar rather than letting a look inherit the defect.
 *
 * This makes a button BIGGER. It hides nothing, moves nothing, and shrinks
 * nothing else — the screen is not weakened to make the look pass.
 * Evidence: build/night-2026-09-04/wT/looks-report.json, key `job_m`.
 * ========================================================================== */

:root[data-look='paper'] .stage-btn.is-open,
:root[data-look='paperfocus'] .stage-btn.is-open {
  min-height: 68px;
  font-size: var(--text-heading);
}
:root[data-look='board'] .stage-btn.is-open {
  min-height: 66px;
  font-size: var(--text-heading);
}
:root[data-look='ledger'] .stage-btn.is-open {
  min-height: 68px;
  font-size: var(--text-heading);
}

/* ==========================================================================
 * The job screen's rung list (`.rung`, jobwork.js's "where this job is").
 *
 * Paper, Paper Focus and Ledger all bet on the same thing — nothing is a card
 * — and the rungs were the one list still drawn as ten bordered boxes under
 * them. Flattening them to hairline rows is the same move already made for
 * `.ts-row`; it removes borders, not information, and the current rung keeps
 * its accent edge so which line the job is on is still the first thing you see.
 * ========================================================================== */

:root[data-look='paper'] .rung,
:root[data-look='paperfocus'] .rung,
:root[data-look='ledger'] .rung {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
}
:root[data-look='paper'] .rung.is-current,
:root[data-look='paperfocus'] .rung.is-current,
:root[data-look='ledger'] .rung.is-current {
  border-left: 3px solid var(--accent);
}
/* Focus keeps its blocks, but they are tinted panels rather than outlined ones. */
:root[data-look='focus'] .rung {
  background: var(--card);
  border-color: transparent;
  border-radius: var(--radius-md);
}
:root[data-look='focus'] .rung.is-current {
  background: var(--accent-soft);
  border-color: var(--accent);
}

/* ==========================================================================
 * The Brand card (src/brand-settings.js) — Settings → Company.
 *
 * It lives in THIS file rather than app.css for one reason: app.css is another
 * lane's file and is being edited tonight, and this is a self-contained block
 * for one card that did not exist before. Everything here is token-driven, so
 * the card is drawn in whichever look the person is running — which is the
 * point being demonstrated: the BRAND is the company's and does not change,
 * the LOOK around it is the person's and does.
 * ========================================================================== */

.bs-card .bs-row {
  padding: var(--space-3) 0;
  border-top: 1px solid var(--line);
}
.bs-card .bs-row:first-of-type {
  border-top: 0;
}
.bs-card .bs-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin: var(--space-2) 0;
}
/* The file input is inside its own label, which IS the button. Hiding it with
 * `display:none` would take it out of the accessibility tree and off the
 * keyboard; this keeps it reachable and invisible. */
.bs-card .bs-file {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.bs-card .bs-pick {
  position: relative;
  cursor: pointer;
}
.bs-card .bs-hex {
  flex: 1;
  min-width: 120px;
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  background: var(--card-2);
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  font: inherit;
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
}
.bs-card .bs-swatch {
  flex: 0 0 var(--tap-min);
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 2px;
  background: var(--card-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.bs-card .bs-note {
  margin-top: var(--space-3);
}

/* The live preview. It is drawn from the DRAFT, not from what is saved, so a
 * colour is judged before it is committed rather than after. */
.bs-preview {
  margin: var(--space-2) 0 var(--space-4);
}
.bs-preview-label {
  display: block;
  font-size: var(--text-micro);
  font-weight: var(--w-bold);
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  color: var(--ink-dim);
  margin-bottom: var(--space-2);
}
.bs-preview-bar {
  --bs-primary: var(--accent);
  --bs-accent: var(--accent);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--card);
}
.bs-preview-logo {
  height: 32px;
  width: auto;
  max-width: 120px;
  object-fit: contain;
  display: block;
}
.bs-preview-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  background: var(--bs-primary);
  color: #fff;
  font-size: var(--text-caption);
  font-weight: var(--w-bold);
}
.bs-preview-name {
  flex: 1;
  min-width: 0;
  font-size: var(--text-body);
  font-weight: var(--w-semi);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bs-preview-btn {
  flex: 0 0 auto;
  padding: 8px var(--space-3);
  border-radius: var(--radius-md);
  background: var(--bs-accent);
  color: #fff;
  font-size: var(--text-label);
  font-weight: var(--w-semi);
}

/* ==========================================================================
 * Settings tabs — nothing hidden, including the tab itself.
 *
 * Adding the Look tab made six, and `.set-tabs` is one of seven strips sharing
 * a single `overflow-x: auto` rule in app.css (:3676). Six content-sized chips
 * measure past 390px, so "Look" and "You" sat one swipe off the right edge —
 * and a person who never finds the tab never learns the board's Look strip
 * exists either. A tab you have to discover by swiping is hidden.
 *
 * The fix is to WRAP rather than scroll, and to give up padding rather than
 * words. Every one of these six labels is already the shortest true name for
 * its screen: "On call" shortened to "Call" reads as the phone in an app that
 * dials customers, and "Time off" to "Off" reads as a toggle. Trading a
 * readable label for a fitted row is one kind of hidden for another. Padding
 * costs nothing, and a second row costs 46px on a screen that scrolls anyway
 * — with every tab on it, at every width, with no gesture to find them.
 *
 * Scoped to `.set-tabs` alone. The other six strips keep the scroll rule,
 * which is right for them: they hold a variable number of chips, this holds
 * exactly six.
 * ========================================================================== */

.set-tabs {
  flex-wrap: wrap;
  overflow-x: visible;
  row-gap: var(--space-2);
}
.set-tabs > .set-tab {
  padding: 0 var(--space-3);
}

/* ==========================================================================
 * GLOVE MODE.  Lane wT, 2026-09-18.
 *
 * The one thing crew.lp got right that Tradescom never built. crew.lp did it
 * with `body.glove-mode` rewriting ~20 individual selectors one at a time
 * (apps/crew-app/index.html:897-916) because crew.lp's CSS was never built on
 * shared tokens. Tradescom's IS — --tap-min/--tap-comfortable/--tap-primary
 * alone already size 59 rules across this file (buttons, chips, `.ts-day`,
 * every `input`/`select`/`textarea`) — so here the "one class re-skins the
 * whole app" trick crew.lp is praised for in DESIGN-STANDARD.md needs three
 * lines, not twenty, because the app was already speaking in tokens. That is
 * the payoff of the token system paying for itself.
 *
 * WHAT TO TOGGLE (for whichever lane wires the Settings control):
 *   ON:  document.documentElement.setAttribute('data-glove', '1')
 *   OFF: document.documentElement.removeAttribute('data-glove')
 * Boolean, not multi-valued — unlike `data-look` there is only one glove
 * state, so presence/absence is the whole API. Persist it exactly the way
 * `src/look.js` persists a look: a synchronous localStorage cache read before
 * first paint (so it doesn't flash off on load), `core.app_users` as the
 * cross-device source of truth once a column exists for it. Follow
 * `src/look.js`'s `paint()`/`clean()`/`sync()` shape rather than inventing a
 * second pattern — same reasoning, same failure modes, same fix.
 *
 * It STACKS with a look — a Truck-look user can also want glove mode, so this
 * is not a seventh look and does not live in `LOOKS` in `src/look.js`. Every
 * rule below is `:root[data-glove='1']`, the same (0,2,0) specificity every
 * look block uses, so when both are set the winner is whichever rule is
 * LATER in this file — which is why this section is the last thing in it.
 * Move it and a look wins ties it should lose.
 *
 * WHY NOT --tap-min ITSELF, EVERYWHERE: `web/README.md`'s rule is "--tap-min
 * is never touched by a LOOK" — a look is a six-way aesthetic preference, and
 * letting one silently shrink the floor was the failure that rule exists to
 * block. Glove mode is not aesthetic. Raising the floor further is the
 * entire feature, on every look at once, which is exactly what overriding
 * the token (rather than fighting one look's numbers at a time) buys.
 *
 * WHAT THIS DELIBERATELY DOES NOT TOUCH: --text-* and --space-*. Rule 04
 * ("It fits" — no scrolling on any stage) is a promise about layout, and
 * bumping the type or space scale ripples through padding, line count and
 * wrap points on every screen in ways nobody can eyeball from here. Bigger
 * tap targets solve the nitrile-glove/sun-glare problem on their own — a
 * glove touches a button, not a paragraph. Two more reasons to leave type
 * alone: --text-body is already 17px, which is crew.lp's own glove-mode
 * INPUT target (17pt) as this app's everyday default; and Spanish copy runs
 * 25-30% longer, so anything sized to fit today's English at a bigger type
 * scale is the first thing that breaks when that ships — see the note in the
 * lane report this section shipped with.
 *
 * WHAT A LOOK STILL WINS ON: a look's own hard-coded sizes on specific
 * selectors (Truck's `.stage-btn { min-height: 66px }`) don't run through
 * these tokens at all, so raising --tap-comfortable does not reach them.
 * `.stage-btn` is called out explicitly below for exactly that reason — it
 * is "the biggest thing on a job" per DESIGN-STANDARD.md and must win
 * regardless of which look, if any, is also active. Any other component that
 * hard-codes its own px instead of a token needs the same explicit line here
 * to be reached by glove mode; the components already listed below are the
 * ones the token sweep couldn't reach on its own.
 * ========================================================================== */

:root[data-glove='1'] {
  --tap-min: 52px;
  --tap-comfortable: 58px;
  --tap-primary: 64px;
  --radius-md: 14px;
}
/* The stage bar — biggest thing on a job, every look, glove or not. Truck's
 * own numbers are 66/78; these clear them so glove wins the tie regardless
 * of which look (if any) is stacked with it. */
:root[data-glove='1'] .stage-btn { min-height: 70px; }
:root[data-glove='1'] .stage-btn.is-open { min-height: 84px; }
/* The bottom bar and its tabs never adopted --tap-min for their own height
 * (padding + font-size only), so raising the token alone doesn't reach them. */
:root[data-glove='1'] .bottombar .bb-item {
  min-height: var(--tap-comfortable);
  padding: var(--space-3) var(--space-3);
}
