/* Sora, served by this app rather than fetched from Google.

   Three reasons, and the first is not cosmetic: a corporate network that
   filters domains blocks fonts.googleapis.com often enough that the headings
   would silently fall back to system-ui and the whole app would look like a
   different product — with nothing in the logs to say why. Self-hosting also
   removes a third-party request from every page load, which is one less thing
   to explain to IT, and drops two DNS round-trips from first paint.

   Latin only, four weights, 98 KB in total. The latin-ext subset doubles that
   for characters no page here uses. */
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/sora-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/sora-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/sora-700.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/static/fonts/sora-800.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ============================================================
   Trader Performance — design system
   Navy authority + warm amber accent. Light and dark themes.
   Class names and --orange/--poor/--navy/--line/--muted tokens
   are preserved for the existing templates.
   ============================================================ */

:root {
  /* neutrals — cool, slight navy bias (chosen, not defaulted) */
  --ground: #eaeef3;
  --card: #ffffff;
  --card-2: #f4f7fa;
  --ink: #16232f;
  --muted: #5d6f80;
  --line: #dbe3ec;
  --line-strong: #c6d2de;

  /* brand — the accent is decimaldatagroup.com's orange, not an approximation
     of it. Changing it here changes it everywhere; nothing hard-codes it. */
  --navy: #10263c;
  --navy-2: #1d4468;
  --accent: #ff5c33;
  --orange: #ff5c33;          /* alias kept for templates */
  --accent-soft: #ff8163;
  --accent-weak: #ffe7e0;

  /* semantic performance bands */
  --exceptional: #1f8a70;
  --good: #2f9e5b;
  --fair: #c1922c;
  --weak: #d1732a;
  --poor: #c0392b;

  /* Leave-calendar marker hues — every kind of thing on the grid gets its own,
     so leave / block-out / pending / holiday / birthday never read alike. */
  --cal-hol: #1f8a70;
  --cal-bday: #7c5cd6;

  /* charts */
  --track: #dde5ec;
  --dot: #1d4468;

  /* role tokens */
  --link: #1d4468;

  /* type scale — the sizes, named. Twelve ad-hoc values used to be scattered
     through the templates (10, 11, 11.5, 12, 12.5, 13, 13.5, 14, 15, 16, 22,
     34), which is accumulation rather than a system. These eight are all sizes
     that were already in use, and every odd value rounds *down* onto one:
     nothing gets wider, so the fixed-layout tables that were fixed to fit the
     page still fit it. Tune the scale here and the whole app follows. */
  --fs-micro: 10px;   /* uppercase micro-labels */
  --fs-tiny: 11px;    /* chips, counts, timestamps */
  --fs-xs: 12px;      /* secondary text */
  --fs-sm: 13px;      /* buttons, dense body */
  --fs-base: 14px;    /* body */
  --fs-md: 15px;      /* section headings */
  --fs-lg: 18px;      /* small figures, page headings */
  --fs-xl: 22px;      /* figures */
  --fs-2xl: 30px;     /* the one big number */

  /* spacing scale */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 36px; --s7: 56px;
  --r-sm: 7px; --r: 11px; --r-lg: 16px;
  --shadow: 0 1px 2px rgba(16,38,60,.05), 0 10px 26px -14px rgba(16,38,60,.22);
  --shadow-lg: 0 1px 2px rgba(16,38,60,.06), 0 24px 50px -22px rgba(16,38,60,.32);
  --ring: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent);

  --topbar: #0d2033;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #1e1e28;
    --card: #272733;
    --card-2: #2e2e3b;
    --ink: #ececf2;
    --muted: #9b9bab;
    --line: #3a3a48;
    --line-strong: #4a4a5a;
    --navy: #0c1e2f;
    --navy-2: #2f6091;
    --accent: #ff6b45;
    --orange: #ff6b45;
    --accent-soft: #ff8f70;
    --accent-weak: #3a2a26;
    --exceptional: #3cb894;
    --good: #46b979;
    --fair: #d7ab52;
    --weak: #e08a4e;
    --poor: #e0685b;
    --cal-hol: #3cb894;
    --cal-bday: #a98ff0;
    --track: #3a3a48;
    --dot: #f4b877;
    --link: #f4b877;
    --shadow: 0 1px 2px rgba(0,0,0,.4), 0 16px 40px -18px rgba(0,0,0,.6);
    --shadow-lg: 0 1px 2px rgba(0,0,0,.5), 0 30px 60px -24px rgba(0,0,0,.7);
    --topbar: #17171f;
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --ground: #1e1e28;
  --card: #272733;
  --card-2: #2e2e3b;
  --ink: #ececf2;
  --muted: #9b9bab;
  --line: #3a3a48;
  --line-strong: #4a4a5a;
  --navy: #0c1e2f;
  --navy-2: #2f6091;
  --accent: #ff6b45;
  --orange: #ff6b45;
  --accent-soft: #ff8f70;
  --accent-weak: #3a2a26;
  --exceptional: #3cb894;
  --good: #46b979;
  --fair: #d7ab52;
  --weak: #e08a4e;
  --poor: #e0685b;
  --cal-hol: #3cb894;
  --cal-bday: #a98ff0;
  --track: #3a3a48;
  /* Links and chart dots stay a soft amber rather than taking the accent.
     In the light theme they are navy — deliberately not the accent — and the
     dark theme should keep that separation: a column of sixty trader names in
     the brand orange makes the accent mean nothing by the time you reach the
     one thing that needed it. */
  --dot: #f4b877;
  --link: #f4b877;
  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 16px 40px -18px rgba(0,0,0,.6);
  --shadow-lg: 0 1px 2px rgba(0,0,0,.5), 0 30px 60px -24px rgba(0,0,0,.7);
  --topbar: #17171f;
  color-scheme: dark;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Display face for headings, brand and headline numbers (falls back to system) */
:root { --font-display: "Sora", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif; }
header.topbar .brand, .card h2, h1, h2, .hero .meta .name,
.gauge-num, .kpi .v, .score-chip, .score { font-family: var(--font-display); }

/* Subtle motion / polish */
.card { transition: border-color .18s ease, box-shadow .18s ease; }
.card:hover { border-color: var(--line-strong); }
.btn { transition: background .15s ease, border-color .15s ease, color .15s ease, transform .04s ease; }
.btn:active { transform: translateY(1px); }
.trader-row { transition: background .12s ease; }
a { transition: color .12s ease; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: color-mix(in srgb, var(--accent) 35%, transparent); }

:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 4px; }

/* ---------- top bar ---------- */
header.topbar {
  position: sticky; top: 0; z-index: 20;
  background: var(--topbar); color: #fff;
  padding: 0 var(--s5);
  height: 54px;
  display: flex; align-items: center; gap: var(--s5);
  border-bottom: 1px solid rgba(255,255,255,.08);
  box-shadow: 0 1px 0 rgba(0,0,0,.15);
}
/* The flare from the wordmark, as a rule rather than a bitmap. It is the one
   distinctive shape the brand owns and it only existed inside the banner PNGs,
   which meant it appeared on two pages. Drawn on a pseudo-element pinned over
   the bar's own bottom edge, so it costs no height and nothing on any page
   moves by a pixel. */
header.topbar::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: linear-gradient(90deg,
              var(--accent) 0%,
              color-mix(in srgb, var(--accent) 55%, transparent) 26%,
              transparent 48%);
  pointer-events: none;
}
/* ---------- the wordmark (templates/_brand.html) ----------
   The supplied artwork, background keyed out, so it drops onto the bar without
   a panel behind it. Sized by height in the template; width follows the file's
   own proportions. */
.dp-brand { display: block; width: auto; }
header.topbar .brand:hover { text-decoration: none; opacity: .9; }
/* The bar holds eleven top-level items for an admin, and below about 1600px they
   did not fit: the overflow pushed the whole document wider than the window, so
   every page on a laptop scrolled sideways and the last two menus sat off the
   right-hand edge, unreachable. The breakpoints further down claw back the width
   first: the suggestion button, the search box and the items themselves each
   give some back.

   NOT by letting the nav scroll. Every dropdown panel is absolutely positioned
   and hangs below the bar, and any `overflow` value on this element makes it a
   clipping context — so the panels are cut off at the bar's own bottom edge and
   not one menu opens. That shipped, briefly. Do not put overflow back here. */
header.topbar nav { display: flex; align-items: center; gap: 2px; margin-left: auto; }
header.topbar nav > a,
header.topbar .menu-btn {
  /* The wordmark and every heading in the app are set in Sora; the nav was the
     one piece of chrome still in the system UI face, which is most of why it
     read as browser furniture rather than part of the product. Sora has no 400
     weight here, so 500 is its lightest — which is the right weight anyway:
     light text on a dark ground optically thins, and 400 system-ui was looking
     washed out against the navy. */
  font-family: var(--font-display); font-weight: 500;
  color: #ccdae7; font-size: 13px; letter-spacing: -.01em;
  padding: 7px 9px; border-radius: 7px;
  background: transparent; border: 0; cursor: pointer;
  line-height: 1; display: inline-flex; align-items: center; gap: 4px;
  white-space: nowrap;                /* kept: the nav was wrapping when narrow */
  transition: background-color .12s ease, color .12s ease;
}
header.topbar nav > a:hover,
header.topbar .menu-btn:hover { color: #fff; text-decoration: none; background: rgba(255,255,255,.08); }
/* "You are here", set the way the marketing site sets it: the label goes
   accent and takes an accent underline, with no fill behind it. The fill used
   to be a flat grey — the same grey as hover — so the two states were hard to
   tell apart and the bar had no relationship to the brand. Both are drawn
   inside the box, so marking the current page still costs no width. */
header.topbar nav > a.active,
header.topbar .menu-btn.active {
  color: var(--accent); background: transparent;
  box-shadow: inset 0 -2px 0 var(--accent);
}
header.topbar nav > a.active:hover,
header.topbar .menu-btn.active:hover { color: var(--accent); background: rgba(255,255,255,.06); }
/* Drawn rather than typed. The ▾ glyph in the markup is a chunky solid triangle
   at whatever the system face makes of it; this is a crisp 7px chevron that
   takes its colour from the item, so it brightens on hover with everything
   else. The glyph itself is hidden by the zero font-size. */
header.topbar .caret {
  font-size: 0; width: 0; height: 0; margin-top: 1px;
  border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor; opacity: .5;
}
header.topbar nav > a:hover .caret, header.topbar .menu-btn:hover .caret,
header.topbar .menu-btn.active .caret { opacity: .8; }

/* Dropdown menus in the top bar */
.topbar .menu { position: relative; }
.topbar .menu-panel {
  position: absolute; top: calc(100% + 6px); right: 0; min-width: 190px;
  display: none; flex-direction: column; gap: 1px; padding: 6px;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 12px 30px rgba(0,0,0,.28); z-index: 60;
}
.topbar .menu.open .menu-panel { display: flex; }
.topbar .menu-panel a {
  color: var(--ink); font-size: 13px; padding: 8px 10px; border-radius: 6px; white-space: nowrap;
}
.topbar .menu-panel a:hover { background: var(--card-2); text-decoration: none; }
.topbar .menu-panel a.active { background: var(--accent-weak); color: var(--accent); font-weight: 600; }
.topbar .menu-label {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
  padding: 4px 10px 6px; border-bottom: 1px solid var(--line); margin-bottom: 4px;
}
/* The floor, not the plan. Below roughly 1300px the eleven items genuinely do
   not fit on one line, and the choice is between a second line, a page that
   scrolls sideways, and menus stranded off the right-hand edge. A taller bar is
   the only one of the three that leaves everything reachable, so the bar grows
   rather than the document. At normal widths nothing wraps and this does
   nothing — the tightening that keeps it on one line lives with the rest of the
   bar's breakpoints, near the end of this file. */
header.topbar { flex-wrap: wrap; height: auto; min-height: 54px; padding-top: 0; padding-bottom: 0; }
@media (max-width: 720px) {
  header.topbar { gap: var(--s3); padding: 0 var(--s3); }
  header.topbar nav { gap: 2px; }
  header.topbar nav > a, header.topbar .menu-btn { padding: 6px 7px; font-size: 12px; }
}
/* Left-side call-to-action link in the top bar (Make a Suggestion). */
.nav-cta { background: var(--accent); color: #16233a; padding: 6px 12px; border-radius: 7px;
  font-size: 12.5px; font-weight: 700; white-space: nowrap; line-height: 1; }
.nav-cta:hover { background: var(--accent-soft); color: #10192b; text-decoration: none; }
.nav-cta.active { box-shadow: 0 0 0 2px rgba(255,255,255,.35) inset; }
/* Mid-width screens: the full label crowds the nav into wrapping — shorten it. */
.nav-cta .cta-short { display: none; }
/* Raised from 1150px. The full label is 90px of a bar that runs out of room at
   1600, and it was still spelling itself out at widths where two whole menus
   had been pushed off the end. The button is no less findable as "Suggest". */
@media (max-width: 1500px) {
  .nav-cta .cta-full { display: none; }
  .nav-cta .cta-short { display: inline; }
}
@media (max-width: 600px) { .nav-cta { padding: 5px 9px; font-size: 12px; } }

/* One-shot decision banners (access request granted/denied, suggestion handled). */
.notice-banner { background: var(--accent-weak); border: 1px solid var(--accent);
  border-left-width: 4px; border-radius: var(--r-sm); color: var(--ink);
  padding: 10px 14px; margin-bottom: 14px; font-size: 13.5px; }

/* Today's public holidays and birthdays — a single strip above the page. */
.day-banner { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-bottom: 14px; font-size: 13px; }
.db-part { border: 1px solid var(--line); border-radius: 999px; padding: 5px 12px;
  background: var(--card); color: var(--ink); }
.db-hol { background: color-mix(in srgb, var(--cal-hol) 16%, var(--card));
  border-color: color-mix(in srgb, var(--cal-hol) 50%, var(--line)); }
.db-bday { background: color-mix(in srgb, var(--cal-bday) 18%, var(--card));
  border-color: color-mix(in srgb, var(--cal-bday) 55%, var(--line)); font-weight: 600; }
.db-bday a { color: var(--ink); text-decoration: underline; }
.db-q { color: var(--fair); font-weight: 700; }

.nav-badge { display: inline-block; min-width: 16px; text-align: center; font-size: 11px;
  font-weight: 700; line-height: 16px; padding: 0 4px; border-radius: 999px;
  background: var(--poor); color: #fff; vertical-align: middle; }
/* In the bar itself these are counts of work waiting, not alarms — and a solid
   red slab was the loudest thing on a navy strip, pulling the eye away from the
   wordmark and from whichever page you are actually on. Tinted, it still reads
   at a glance without shouting. The solid fill is kept inside the dropdown
   panels, where the background is light and a tint would disappear. */
header.topbar nav > a .nav-badge,
header.topbar .menu-btn .nav-badge {
  background: color-mix(in srgb, var(--poor) 30%, transparent);
  color: #ffc9c2; font-size: 10.5px; min-width: 15px; line-height: 15px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--poor) 60%, transparent);
}
header.topbar nav > a:hover .nav-badge, header.topbar .menu-btn:hover .nav-badge,
header.topbar nav > a.active .nav-badge, header.topbar .menu-btn.active .nav-badge { color: #fff; }
.mon-colour { display: inline-block; font-size: 11px; font-weight: 700; padding: 1px 8px;
  border-radius: 999px; color: #10202e; }
/* Row flagged as a likely mismatch (e.g. a code that matches no rota/leave rows) */
tr.row-warn > td { background: color-mix(in srgb, var(--poor) 9%, transparent); }

/* Tall scrollable table with a sticky header (full entry lists) */
.tall-scroll { max-height: 70vh; overflow: auto; border: 1px solid var(--line); border-radius: 8px; }
.tall-scroll table { margin: 0; }
.tall-scroll thead th { position: sticky; top: 0; z-index: 1; background: var(--card-2); }

/* Soft-skill ratings grid */
.soft-grid td, .soft-grid th { padding: 6px 10px; vertical-align: middle; }
.soft-grid td:first-child { font-weight: 600; }
.soft-grid input[type="number"] { text-align: center; }

/* Rota viewer */
.rota-day { margin-top: 14px; }
.rota-date { font-size: 15px; margin: 0 0 10px; display: flex; align-items: center; gap: 4px; }
/* Fits the page rather than scrolling sideways: fixed layout with proportional
   columns, and long competition / match names wrap instead of pushing out. */
.rota-table { table-layout: fixed; width: 100%; }
.rota-table th, .rota-table td { padding: 7px 8px; overflow-wrap: break-word; }
.rota-table th:nth-child(1), .rota-table td:nth-child(1) { width: 9%; }    /* Time */
.rota-table th:nth-child(2), .rota-table td:nth-child(2) { width: 13%; }   /* Competition */
.rota-table th:nth-child(3), .rota-table td:nth-child(3) { width: 14%; }   /* Match */
.rota-table th:nth-child(4), .rota-table td:nth-child(4) { width: 8%; }    /* Country */
.rota-table th:nth-child(5), .rota-table td:nth-child(5) { width: 8%; }    /* Format */
.rota-table th:nth-child(6), .rota-table td:nth-child(6) { width: 5%; }    /* Cat */
.rota-table th:nth-child(7), .rota-table td:nth-child(7) { width: 15%; }   /* Traders */
.rota-table th:nth-child(8), .rota-table td:nth-child(8) { width: 8%; }    /* Support */
.rota-table th:nth-child(9), .rota-table td:nth-child(9) { width: 8%; }    /* Training */
.rota-table th:nth-child(10), .rota-table td:nth-child(10) { width: 7%; }  /* Profit */
.rota-table th:nth-child(11), .rota-table td:nth-child(11) { width: 5%; }  /* ROI */
.rota-chip { display: inline-block; font-size: 12px; padding: 2px 8px; margin: 1px 3px 1px 0;
  border-radius: 999px; background: var(--card-2); border: 1px solid var(--line);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  vertical-align: bottom; }
.rota-chip.mine { background: var(--accent-weak); border-color: var(--accent); color: var(--accent); font-weight: 700; }
.rota-chip.hit { background: var(--accent-weak); border-color: var(--accent); color: var(--accent); font-weight: 700; box-shadow: 0 0 0 1px var(--accent); }
.rota-chip .role-tag { display: inline-block; font-size: 9.5px; font-weight: 800; letter-spacing: .03em;
  padding: 0 4px; margin-right: 5px; border-radius: 4px; background: var(--navy, #0d2033); color: #fff;
  vertical-align: middle; }
:root:not([data-theme="light"]) .rota-chip .role-tag { background: #2b4256; }

/* Highlighted (picked) table row */
tr.row-hi > td { background: var(--accent-weak); }
tr.row-hi > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

/* Analytics insight cards */
.insight-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-top: 4px; }
.insight-card { background: var(--card-2); border: 1px solid var(--line); border-left-width: 3px;
  border-radius: 10px; padding: 12px 14px; }
.insight-card.watch { border-left-color: var(--fair); }
.insight-card.info { border-left-color: var(--accent); }
.insight-head { font-weight: 700; font-size: 13.5px; color: var(--ink); margin-bottom: 4px; }
.insight-detail { font-size: 12.5px; color: var(--muted); line-height: 1.5; }

/* Distribution strip / beeswarm */
.chart.strip { width: 100%; height: auto; }
.strip-band { opacity: .14; }
.strip-band.band-Exceptional { fill: var(--exceptional); }
.strip-band.band-Good { fill: var(--good); }
.strip-band.band-Fair { fill: var(--fair); }
.strip-band.band-Weak { fill: var(--weak); }
.strip-band.band-Poor { fill: var(--poor); }
.strip-dot { fill: var(--muted); opacity: .5; }
.strip-you-halo { fill: var(--accent); opacity: .22; }
.strip-you { fill: var(--accent); stroke: var(--card); stroke-width: 1.5; }
.strip-you-lbl { fill: var(--ink); font-weight: 800; }

/* Peer-context chips in the trader hero */
.peer-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.peer-chip { font-size: 12px; padding: 2px 9px; border-radius: 999px;
  background: var(--card-2); border: 1px solid var(--line); color: var(--ink); }
.peer-chip b { font-variant-numeric: tabular-nums; }
.peer-chip.up { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, var(--line)); }
.peer-chip.down { color: var(--poor); border-color: color-mix(in srgb, var(--poor) 45%, var(--line)); }

/* Drivers: contribution bars (band-coloured, labelled) */
.driver-row { display: grid; grid-template-columns: 118px 1fr 42px; align-items: center;
  gap: 10px; padding: 3px 0; }
.driver-name { font-size: 13px; color: var(--ink); }
.driver-track { height: 10px; background: var(--card-2); border: 1px solid var(--line);
  border-radius: 999px; overflow: hidden; }
.driver-fill { display: block; height: 100%; border-radius: 999px; min-width: 2px; }
.driver-pts { font-size: 12.5px; font-weight: 700; text-align: right;
  font-variant-numeric: tabular-nums; color: var(--ink); }
.driver-total { border-top: 1px solid var(--line); margin-top: 6px; padding-top: 8px; }
.driver-total .driver-name, .driver-total .driver-pts { font-weight: 800; }
.driver-total .driver-track { background: transparent; border: 0; }
.band-Exceptional { background: var(--exceptional); }
.band-Good { background: var(--good); }
.band-Fair { background: var(--fair); }
.band-Weak { background: var(--weak); }
.band-Poor { background: var(--poor); }

/* Levers: the opportunity headline + ranked list */
.lever-headline { display: flex; gap: 14px; align-items: flex-start;
  background: var(--card-2); border: 1px solid var(--line); border-radius: 10px;
  padding: 12px 14px; font-size: 13.5px; line-height: 1.5; }
.lever-headline-num { font-family: var(--font-display, inherit); font-weight: 800;
  font-size: 26px; line-height: 1; color: var(--accent); white-space: nowrap; }
.lever-headline-num span { font-size: 12px; font-weight: 600; margin-left: 3px; color: var(--muted); }
.lever-list { list-style: none; margin: 12px 0 0; padding: 0; }
.lever-list li { display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; padding: 7px 0; border-top: 1px solid var(--line); font-size: 13px; }
.lever-gain { font-weight: 700; color: var(--accent); font-variant-numeric: tabular-nums; white-space: nowrap; }

.mon-green { background: var(--good); }
.mon-amber { background: var(--fair); }
.mon-red { background: var(--poor); color: #fff; }
.theme-toggle {
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.16);
  color: #dfe8f0; width: 32px; height: 32px; border-radius: 8px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; font-size: 15px;
  transition: background .15s ease;
}
.theme-toggle:hover { background: rgba(255,255,255,.16); }

.wrap { max-width: 1140px; margin: 0 auto; padding: var(--s5); }

/* ---------- headings ---------- */
h1, h2, h3 { text-wrap: balance; }

/* ---------- cards ---------- */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s5);
  margin-bottom: var(--s5);
  box-shadow: var(--shadow);
}
.card h2 {
  margin: 0 0 var(--s4);
  font-size: 11px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink); font-weight: 700;
  padding-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: 8px;
}

/* The brand's signature: every headline on decimaldatagroup.com ends in an
   orange full stop. Here the label wears the ink and the stop carries the
   accent, which is how the site does it — and it also lifts these headings off
   the 3.2:1 they used to sit at when the whole line was accent-coloured.
   `.lbl` exists so the stop lands against the last letter: this heading is a
   flex row with a gap, so a stop added as its own child gets pushed away from
   the word, and the .13em tracking is applied *after* the final character,
   which pushes it further still. Hence the negative margin, and the size bump
   to survive at 11px. */
.card h2 .lbl::after {
  content: ".";
  color: var(--accent);
  letter-spacing: 0;
  margin-left: -.07em;
  font-size: 1.75em;
  line-height: 0;
}

.grid { display: grid; gap: var(--s5); }
/* minmax(0, 1fr), not 1fr: a plain 1fr track can't shrink below its content's
   min-content width, so one wide card pushes the whole grid past the page. */
.grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 860px) { .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: minmax(0, 1fr); } }
/* Side-by-side only when there's genuinely room. Used where one column holds a
   wide table that gets unusable — or spills its card — once squeezed. */
@media (max-width: 1500px) { .grid.split-late { grid-template-columns: minmax(0, 1fr); } }
/* Small tables sharing a three-across row (By format / By country / By comp)
   run out of room before the layout collapses — tighten the cells instead of
   letting them push out of their card. */
@media (max-width: 1360px) {
  .grid.cols-3 .card th, .grid.cols-3 .card td { padding: 7px 6px; font-size: 12.5px; }
}

/* ---------- hero ---------- */
.hero {
  position: relative; overflow: hidden;
  background:
    radial-gradient(120% 140% at 85% -20%, rgba(211,120,31,.28), transparent 55%),
    linear-gradient(150deg, var(--navy), var(--navy-2));
  color: #fff; border-radius: var(--r-lg);
  padding: var(--s6) var(--s6);
  margin-bottom: var(--s5);
  display: flex; align-items: center; gap: var(--s6);
  box-shadow: var(--shadow-lg);
}
.hero .score {
  font-size: 60px; font-weight: 800; line-height: .95;
  font-variant-numeric: tabular-nums; letter-spacing: -.03em;
}
.hero .score small { font-size: 20px; color: #a9bccd; font-weight: 500; letter-spacing: 0; }
.hero .meta { flex: 1; min-width: 0; }
.hero .meta .name { font-size: 24px; font-weight: 700; letter-spacing: -.01em; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.hero .meta .sub { color: #b6c5d3; font-size: 13px; margin-top: 6px; }

/* ---------- band badges ---------- */
.badge {
  display: inline-flex; align-items: center;
  padding: 3px 11px; border-radius: 999px; font-size: 11.5px;
  font-weight: 700; letter-spacing: .02em; color: #fff; white-space: nowrap;
}
.badge.Exceptional { background: var(--exceptional); }
.badge.Good { background: var(--good); }
.badge.Fair { background: var(--fair); }
.badge.Weak { background: var(--weak); }
.badge.Poor { background: var(--poor); }

/* ---------- tables ---------- */
table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); }
thead th {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .09em;
  color: var(--muted); font-weight: 700;
  background: var(--card-2);
}
tbody tr { transition: background .12s ease; }
tbody tr:hover { background: color-mix(in srgb, var(--accent) 6%, var(--card)); }
tfoot th { border-top: 2px solid var(--line-strong); border-bottom: none; padding-top: 12px; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
tr:last-child td { border-bottom: none; }

/* ---------- score bar ---------- */
.bar { height: 8px; background: var(--track); border-radius: 999px; overflow: hidden; }
.bar > span { display: block; height: 100%; background: linear-gradient(90deg, var(--accent-soft), var(--accent)); border-radius: 999px; }

/* ---------- misc text ---------- */
.story { font-size: 15px; line-height: 1.75; white-space: pre-wrap; letter-spacing: .002em; }
.muted { color: var(--muted); }
.pill {
  display: inline-block; background: var(--card-2); border: 1px solid var(--line);
  border-radius: 999px; padding: 2px 10px; font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* Collapsible month groups (manual-entry Data page) */
.month-groups { margin-top: 16px; display: flex; flex-direction: column; gap: 8px; }
.month-group { border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
.month-group > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; background: var(--card-2); font-size: 13px; font-weight: 600;
}
.month-group > summary::-webkit-details-marker { display: none; }
.month-group > summary::before {
  content: "▸"; color: var(--muted); font-size: 11px; transition: transform .15s ease;
}
.month-group[open] > summary::before { transform: rotate(90deg); }
.month-group > summary:hover { background: color-mix(in srgb, var(--accent) 8%, var(--card-2)); }
.month-group .mg-label { color: var(--ink); }
.month-group .mg-meta { margin-left: auto; color: var(--muted); font-weight: normal;
  font-size: 12px; font-variant-numeric: tabular-nums; }
.month-group table { margin: 0; }
.month-group table th { position: static; }

/* Trader Score gauge (review hero) */
.gauge-wrap { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; }
.gauge { width: 148px; height: 148px; display: block; }
.gauge-track { stroke: rgba(255, 255, 255, .15); }
.gauge-num { font-size: 42px; font-weight: 800; fill: #fff; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.gauge-lbl { font-size: 12px; fill: rgba(255, 255, 255, .6); }
.g-exceptional { stroke: var(--exceptional); } .g-good { stroke: var(--good); }
.g-fair { stroke: var(--fair); } .g-weak { stroke: var(--weak); } .g-poor { stroke: var(--poor); }

/* Trader avatars (initials) */
.avatar { display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%; background: hsl(var(--h), 42%, 42%);
  color: #fff; font-size: 12px; font-weight: 700; flex: 0 0 auto; margin-right: 9px;
  vertical-align: middle; letter-spacing: .02em; }
.row-spark { display: inline-flex; align-items: center; }

/* Trend sparkline (trader list) */
.spark { width: 92px; height: 26px; display: block; }
.spark polyline { stroke: var(--muted); } .spark circle { fill: var(--muted); }
.spark-up polyline { stroke: var(--good); } .spark-up circle { fill: var(--good); }
.spark-down polyline { stroke: var(--poor); } .spark-down circle { fill: var(--poor); }

.delta-up { color: var(--good); font-weight: 600; }
.delta-down { color: var(--poor); font-weight: 600; }
.cell-ok { color: var(--good); font-weight: 700; }
.cell-miss { color: var(--poor); font-weight: 700; }

/* Review sign-off + comments */
.signoff-done { color: var(--good); font-size: 13px; font-weight: 600; margin-top: 4px; }
.comment-list { display: flex; flex-direction: column; gap: 8px; }
.comment { background: var(--card-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 8px 11px; }
.comment-head { display: flex; align-items: center; gap: 8px; font-size: 12px; margin-bottom: 3px; }
.comment-body { font-size: 13.5px; white-space: pre-wrap; color: var(--ink); }
textarea { width: 100%; background: var(--card-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); color: var(--ink); padding: 8px 10px; font: inherit; font-size: 13px; resize: vertical; }
textarea:focus { outline: none; border-color: var(--accent); }

/* At-risk flags on the trader list */
.risk-chip { display: inline-block; font-size: 11px; padding: 1px 8px; border-radius: 999px;
  margin-left: 6px; font-weight: 600; white-space: nowrap; }
.risk-chip.risk-low { background: var(--poor); color: #fff; }
.risk-chip.risk-decline { background: var(--fair); color: #2b2205; }
.pill.risk-low { background: var(--poor); color: #fff; border-color: var(--poor); }

/* Data-required (to-do) trader chips */
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 6px; }
.todo-chip { display: inline-flex; align-items: center; gap: 6px; font-size: 13px;
  padding: 4px 10px; border-radius: 999px; text-decoration: none; border: 1px solid var(--line);
  color: var(--ink); background: var(--card); }
.todo-chip span { font-size: 11px; font-weight: 600; opacity: .85; }
.todo-chip.overdue, .todo-chip.missing { border-color: var(--poor); }
.todo-chip.overdue span, .todo-chip.missing span { color: var(--poor); }
.todo-chip.due { border-color: var(--fair); }
.todo-chip.due span { color: var(--fair); }
.todo-chip:hover { border-color: var(--orange); }
/* Trader name inside a cadence chip — the chip can't be a link (it holds the
   "no entry" form), so the name itself carries the link. */
.todo-chip .chip-name { color: inherit; font-weight: inherit; }
.todo-chip .chip-name:hover { color: var(--accent); text-decoration: underline; }
.chip-x { border: none; background: none; cursor: pointer; font-size: 10px;
  font-weight: 600; text-transform: uppercase; letter-spacing: .04em; padding: 0 0 0 2px;
  color: var(--muted); opacity: .7; }
.chip-x:hover { color: var(--orange); opacity: 1; text-decoration: underline; }

/* Rota builder */
table.rota-build td, table.rota-build th { vertical-align: middle; }
table.rota-build tr.rota-day td { background: var(--accent-weak); font-size: 13px;
  padding-top: 8px; padding-bottom: 8px; }
table.rota-build tr.has-warn { background: color-mix(in srgb, var(--poor) 7%, transparent); }
.rota-warn { color: var(--poor); font-size: 11px; margin-top: 3px; }
/* Slot pickers sized to hold a full trader name without clipping. */
select.rota-slot { width: 168px; max-width: 168px; font-size: 12.5px; padding: 5px 6px; }
/* Fixture details run across the top of each fixture; the select row sits tight
   under it, so each fixture is two compact lines instead of a tall column. */
table.rota-build tr.fixhead td { border-top: 1px solid var(--line); padding: 9px 8px 1px; }
table.rota-build tr.fixrow td { padding: 1px 8px 9px; vertical-align: middle; }
.risk-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 13px;
  color: var(--ink); padding: 7px 8px; cursor: pointer; user-select: none; }

/* Trader-list search / filter / sort controls */
.list-controls { display: flex; flex-wrap: wrap; gap: 10px; margin: 2px 0 14px; }
/* Filters sit side by side, each only as wide as it needs — the global
   width:100% on form controls would otherwise stack them a row apiece. */
.list-controls input, .list-controls select {
  width: auto; flex: 0 1 auto; max-width: 100%;
  padding: 7px 10px; font-size: 13px; border: 1px solid var(--line);
  border-radius: var(--r-sm); background: var(--card-2); color: var(--ink);
}
.list-controls #tp-search { flex: 1 1 180px; min-width: 160px; }

/* Print / Save-as-PDF: force a clean light layout, drop the app chrome */
.print-only { display: none; }

@media print {
  :root, :root[data-theme="dark"], :root:not([data-theme="light"]) {
    --ground: #fff; --card: #fff; --card-2: #f6f8fa; --ink: #16232f; --muted: #555;
    --line: #cfd8e0; --line-strong: #9aa7b3; --track: #dde3e9;
  }
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  body { background: #fff !important; font-size: 12px; }
  .topbar, .theme-toggle, .no-print, form, .btn, button, .list-controls,
  .hero-banner { display: none !important; }
  .print-only { display: block !important; }
  .wrap { max-width: none; margin: 0; padding: 0; }
  .card { box-shadow: none !important; border: 1px solid #d5dce2; break-inside: avoid; margin-bottom: 12px; padding: 12px 14px; }
  .grid.cols-2, .grid.cols-4, .chart-grid-2, .month-group, .driver-row, .lever-headline, tr { break-inside: avoid; }
  h2, h3 { break-after: avoid; }
  svg.chart { max-height: 200px; }
  a { color: inherit; text-decoration: none; }
  @page { margin: 13mm; }

  /* Review-sheet title block */
  .print-header { margin-bottom: 12px; padding-bottom: 8px; border-bottom: 2px solid var(--ink); }
  .print-header .ph-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; }
  .print-header .ph-title { font-family: var(--font-display, inherit); font-weight: 800; font-size: 19px; color: var(--ink); }
  .print-header .ph-sub { font-size: 11px; color: var(--muted); margin-top: 2px; }
  .print-header .ph-score { font-family: var(--font-display, inherit); font-weight: 800; font-size: 26px; color: var(--ink); white-space: nowrap; }
  .print-header .ph-score span { font-size: 12px; font-weight: 700; color: var(--muted); margin-left: 4px; }
  /* The on-screen hero is redundant with the print header — keep it tight */
  .hero { border: 0 !important; padding: 0 0 8px !important; margin-bottom: 8px; break-inside: avoid; }
}

.flag {
  background: color-mix(in srgb, var(--accent) 10%, var(--card));
  border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--line));
  border-left: 3px solid var(--accent);
  padding: 10px 14px; font-size: 13px; border-radius: var(--r-sm);
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--accent); color: #fff; border: 1px solid transparent;
  padding: 8px 16px; border-radius: var(--r-sm); font-size: 13px; cursor: pointer;
  font-weight: 700; letter-spacing: .01em; white-space: nowrap;
  transition: filter .15s ease, transform .04s ease;
}
.btn:hover { filter: brightness(1.06); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn.ghost {
  background: transparent; color: var(--ink); border: 1px solid var(--line-strong);
}
.btn.ghost:hover { background: var(--card-2); filter: none; }

/* ---------- forms ---------- */
input, select, textarea {
  font: inherit; padding: 8px 10px; width: 100%;
  color: var(--ink); background: var(--card);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  transition: border-color .15s ease, box-shadow .15s ease;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
label { font-size: 11.5px; color: var(--muted); display: block; margin-bottom: 5px; letter-spacing: .02em; text-transform: uppercase; }
.field { margin-bottom: var(--s4); }
.row { display: flex; gap: var(--s4); flex-wrap: wrap; }
.row > .field { flex: 1; min-width: 140px; }

/* Inline entry forms (free-text logs): keep every control on one baseline even
   when a label wraps to two lines. Each field is a flex column stretched to the
   tallest field's height, with the control pinned to the bottom. */
.entry-form .row { align-items: stretch; }
.entry-form .row > .field { display: flex; flex-direction: column; }
.entry-form .row > .field > label { margin-bottom: 5px; }
.entry-form .row > .field > input,
.entry-form .row > .field > select,
.entry-form .row > .field > textarea { margin-top: auto; }
.entry-form textarea { min-height: 38px; }

/* Uniform grid for forms with many fields (e.g. Competition Leadership): every
   field is an equal-width cell that wraps into a tidy matrix, inputs bottom-
   aligned per row, and the action button sits on its own row below — never
   floating off the edge. */
.entry-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px; align-items: end; }
.entry-grid .field { margin-bottom: 0; display: flex; flex-direction: column; }
.entry-grid .field > label { margin-bottom: 5px; }
.entry-grid .field > input,
.entry-grid .field > select,
.entry-grid .field > textarea { margin-top: auto; }
.entry-grid .field.span2 { grid-column: span 2; }
/* A field that always takes a row to itself, whatever the column count. */
.entry-grid .field.spanall { grid-column: 1 / -1; }
.entry-grid textarea { min-height: 38px; }
.entry-actions { margin-top: 14px; }
@media (max-width: 640px) { .entry-grid .field.span2 { grid-column: auto; } }

/* ---------- trader list ---------- */
.trader-row td .name { font-weight: 700; }
.trader-row td .name:hover { color: var(--accent); }
/* Trader cell: avatar, name/code and the risk chips all stay on one line. They
   used to be allowed to wrap, which made a handful of rows two lines tall and
   left the rest of the table stepping up and down as you read down it. */
.trader-cell { display: flex; align-items: center; gap: 6px 8px; flex-wrap: nowrap; }
.trader-main { display: inline-flex; align-items: center; gap: 8px; }
.trader-cell .avatar { flex: none; }
.trader-id { white-space: nowrap; }
/* Compact traders table so every (no-wrap) column fits without horizontal scroll. */
#tp-table th, #tp-table td { padding: 9px 8px; }
#tp-table th:first-child, #tp-table td:first-child { padding-left: 4px; }
#tp-table .score-mini { gap: 7px; }
#tp-table .score-mini .bar { min-width: 40px; max-width: 90px; }
#tp-table .row-actions { font-size: 12px; white-space: nowrap; }
/* One line per trader, every row the same height, so the eye can run straight
   down a column of sixty without it stepping — and the whole table inside the
   page, because nowrap on its own just pushes the last column off the edge.
   Fixed layout is what makes that possible: the columns are given shares of
   the width and long values are trimmed, rather than each column widening to
   fit its longest value and the total overflowing. */
#tp-table { table-layout: fixed; width: 100%; }
#tp-table tbody tr, #tp-table tbody td { height: 44px; }
#tp-table th, #tp-table td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#tp-table th:nth-child(1), #tp-table td:nth-child(1) { width: 21%; }
#tp-table th:nth-child(2), #tp-table td:nth-child(2) { width: 13%; }
#tp-table th:nth-child(3), #tp-table td:nth-child(3) { width: 15%; }
#tp-table th:nth-child(4), #tp-table td:nth-child(4) { width: 11%; }
#tp-table th:nth-child(5), #tp-table td:nth-child(5) { width: 14%; }
#tp-table th:nth-child(6), #tp-table td:nth-child(6) { width: 12%; }
#tp-table th:nth-child(7), #tp-table td:nth-child(7) { width: 14%; }
/* The name is the one thing worth trimming last, so it gets the whole cell. */
#tp-table .trader-cell { min-width: 0; }
#tp-table .trader-id { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.no-data-flag { font-size: 13px; cursor: help; }

/* Narrow screens: the fixed shares stop being readable long before they stop
   fitting, so hand the table back its natural widths and let the card scroll. */
@media (max-width: 900px) {
  #tp-table { table-layout: auto; }
  #tp-table th[class], #tp-table td[class],
  #tp-table th:nth-child(n), #tp-table td:nth-child(n) { width: auto; }
}
.score-chip { font-weight: 800; font-variant-numeric: tabular-nums; }
.score-mini { display: flex; align-items: center; gap: 10px; }
.score-mini .bar { flex: 1; min-width: 60px; max-width: 130px; }

/* ---------- KPI tiles ---------- */
.kpi {
  text-align: center; padding: var(--s4) var(--s3);
  background: var(--card-2); border: 1px solid var(--line); border-radius: var(--r);
}
/* A KPI tile that links somewhere (e.g. "need attention" -> filtered list). */
a.kpi-link { display: block; color: inherit; transition: border-color .12s ease; }
a.kpi-link:hover { text-decoration: none; border-color: var(--accent); }

/* Trader Score heatmap (analytics): trader × month, band-tinted cells. */
.hm-table td.hm { text-align: center; font-variant-numeric: tabular-nums;
  font-size: 12px; font-weight: 600; padding: 6px 8px; }
.hm-table td.hm-none { text-align: center; color: var(--muted); }
.hm-Exceptional { background: color-mix(in srgb, var(--exceptional) 32%, var(--card)); }
.hm-Good { background: color-mix(in srgb, var(--good) 28%, var(--card)); }
.hm-Fair { background: color-mix(in srgb, var(--fair) 26%, var(--card)); }
.hm-Weak { background: color-mix(in srgb, var(--weak) 26%, var(--card)); }
.hm-Poor { background: color-mix(in srgb, var(--poor) 30%, var(--card)); }

/* "On this page" jump chips (long trader review pages). */
.jump-chips { display: flex; flex-wrap: wrap; gap: 7px; align-items: center;
  margin: -6px 0 16px; }
.jump-chips a { font-size: 12px; padding: 3px 11px; border-radius: 999px;
  background: var(--card); border: 1px solid var(--line); color: var(--ink); }
.jump-chips a:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
/* Used as a section switcher (Training), the chip for the page you're on is
   filled rather than outlined. */
.jump-chips a.active { background: var(--accent-weak); border-color: var(--accent);
  color: var(--accent); font-weight: 700; }
.jump-chips a.active .pill { border-color: var(--accent); color: var(--accent);
  background: transparent; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
/* Anchored sections land below the sticky top bar, not underneath it. */
.card[id], .rota-day[id] { scroll-margin-top: 66px; }

/* Scouted fixtures: a quiet but distinct marker on the schedule, and the
   builder's per-fixture toggle. */
.scout-pill { background: color-mix(in srgb, var(--navy-2) 18%, var(--card-2));
  border-color: var(--navy-2); color: var(--navy-2); font-weight: 700; }
:root[data-theme="dark"] .scout-pill,
:root:not([data-theme="light"]) .scout-pill { color: var(--accent-soft); border-color: var(--accent-soft); }
.scout-toggle { display: inline-flex; align-items: center; gap: 5px; margin: 0;
  font-size: 11.5px; color: var(--muted); text-transform: none; letter-spacing: 0;
  cursor: pointer; white-space: nowrap; }
.scout-toggle input { margin: 0; }

/* Summary page: the Trader Score panel centres in whatever height its
   neighbour sets, instead of stranding the number at the top. */
.score-card { display: flex; flex-direction: column; text-align: center; }
.score-card .sc-body { flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 4px; padding: 8px 0; }
/* Speedometer — sits on a card, so it takes theme tokens rather than the
   white-on-navy of the hero gauge. */
.speedo { width: 100%; max-width: 280px; height: auto; display: block; }
.sp-track { stroke: var(--track); }
.sp-zone { stroke-opacity: .85; }
.sp-zone.z-poor { stroke: var(--poor); }
.sp-zone.z-weak { stroke: var(--weak); }
.sp-zone.z-fair { stroke: var(--fair); }
.sp-zone.z-good { stroke: var(--good); }
.sp-zone.z-exceptional { stroke: var(--exceptional); }
.sp-tick { stroke: var(--card); stroke-width: 2; }
.sp-tick-lbl { font-size: 10px; fill: var(--muted); font-variant-numeric: tabular-nums; }
.sp-needle { fill: var(--ink); }
.sp-hub { fill: var(--ink); stroke: var(--card); stroke-width: 3; }
.sp-num { font-family: var(--font-display); font-size: 40px; font-weight: 800;
  fill: var(--ink); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.sp-of { font-size: 11px; fill: var(--muted); letter-spacing: .04em;
  text-transform: uppercase; }

/* ---------- Users ----------
   Each row carries several small forms (email, role, permissions, password),
   so they're laid out as tight inline pairs rather than stacked full-width. */
.users-table td { vertical-align: top; }
.users-table .ut-inline { display: flex; gap: 5px; align-items: center; margin: 0; }
.users-table .ut-inline input, .users-table .ut-inline select {
  padding: 4px 7px; font-size: 12px; min-width: 0; }
.users-table .ut-inline input { flex: 1 1 auto; }
.users-table .ut-inline select { flex: 1 1 auto; max-width: 170px; }
.users-table .ut-inline .btn { padding: 3px 9px; font-size: 11px; white-space: nowrap; }
.users-table th:nth-child(1), .users-table td:nth-child(1) { min-width: 190px; }
.users-table th:nth-child(2), .users-table td:nth-child(2) { min-width: 190px; }
.users-table th:nth-child(3), .users-table td:nth-child(3) { min-width: 170px; }
.users-table th:nth-child(4), .users-table td:nth-child(4) { min-width: 170px; }

/* ---------- Quick edit (all traders, one screen) ----------
   Fixed layout with proportional columns and full-width controls, so the table
   fits the page instead of scrolling sideways. */
.qe-wrap { overflow-x: auto; }
.qe-table { table-layout: fixed; width: 100%; }
.qe-table th, .qe-table td { padding-left: 8px; padding-right: 8px; }
.qe-table th:nth-child(1), .qe-table td:nth-child(1) { width: 16%; }
.qe-table th:nth-child(2), .qe-table td:nth-child(2) { width: 9%; }
.qe-table th:nth-child(3), .qe-table td:nth-child(3) { width: 12%; }
.qe-table th:nth-child(4), .qe-table td:nth-child(4) { width: 8%; }
.qe-table th:nth-child(5), .qe-table td:nth-child(5) { width: 10%; }
.qe-table th:nth-child(6), .qe-table td:nth-child(6) { width: 15%; }
.qe-table th:nth-child(7), .qe-table td:nth-child(7) { width: 17%; }
.qe-table th:nth-child(8), .qe-table td:nth-child(8) { width: 13%; }
.qe-table input, .qe-table select { width: 100%; min-width: 0; }
.qe-table input[type="checkbox"] { width: auto; }
.qe-table .name { overflow-wrap: anywhere; }
.qe-table label { white-space: normal; }

/* ---------- Leave calendar ---------- */
table.cal { table-layout: fixed; }
table.cal th { text-align: center; font-size: 10.5px; }
.cal-cell { vertical-align: top; height: 96px; width: 14.28%; padding: 5px 6px;
  border: 1px solid var(--line); }
.cal-cell.out { background: var(--card-2); opacity: .55; }
.cal-cell.today { box-shadow: inset 0 0 0 2px var(--accent); }
/* Wraps because a day like Christmas carries a marker for every country. */
.cal-date { font-size: 11px; font-weight: 700; color: var(--muted);
  display: flex; align-items: center; flex-wrap: wrap; gap: 3px 5px; margin-bottom: 3px; }
.cal-n { background: var(--accent-weak); color: var(--accent); border-radius: 999px;
  padding: 0 5px; font-size: 10px; }
/* Every marker in a day cell — leave, block-out, pending, public holiday,
   birthday — shares one metric, so the rows line up whatever a day holds. */
.cal-chip, .cal-bday, .cal-hol {
  font-size: 10.5px; line-height: 15px; min-height: 17px; padding: 0 6px;
  border-radius: 5px; margin-bottom: 3px; border: 1px solid var(--line);
  color: var(--ink); box-sizing: border-box; }
.cal-chip, .cal-bday { display: block; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; text-decoration: none; }

/* Leave — the default, warm amber. */
.cal-chip { background: color-mix(in srgb, var(--accent) 30%, var(--card));
  border-color: color-mix(in srgb, var(--accent) 65%, var(--line)); }
.cal-chip:hover { text-decoration: none; border-color: var(--accent); }
/* Block-out — blue. */
.cal-chip.block { background: color-mix(in srgb, var(--navy-2) 30%, var(--card));
  border-color: color-mix(in srgb, var(--navy-2) 70%, var(--line)); }
/* Pending — grey and unfilled. Undecided reads as undecided, and it borrows no
   hue from the four kinds of thing that ARE decided. */
.cal-chip.pending { background: transparent; color: var(--muted);
  border: 1px dashed var(--line-strong); }
.cal-chip.pending:hover { color: var(--ink); border-color: var(--muted); }
/* Birthday — violet. */
.cal-bday { font-weight: 600;
  background: color-mix(in srgb, var(--cal-bday) 22%, var(--card));
  border-color: color-mix(in srgb, var(--cal-bday) 60%, var(--line)); }
.cal-bday:hover { text-decoration: none; border-color: var(--cal-bday); }
/* Public holiday — green. Country code(s) plus what the day actually is. */
.cal-hol { display: flex; flex-wrap: nowrap; align-items: center; gap: 4px;
  cursor: help; background: color-mix(in srgb, var(--cal-hol) 20%, var(--card));
  border-color: color-mix(in srgb, var(--cal-hol) 60%, var(--line)); }
/* nowrap, so the name shrinks and ellipsises beside the code instead of
   dropping to a second line — a marker is always exactly one row tall. */
.cal-hol .cc { flex: 0 0 auto; }
.cal-hol.prov { border-style: dashed; }
/* One line, ellipsised: "Prophet Muhammad's birthday" must not stretch the row.
   The full, unabbreviated name is on the marker's tooltip. */
.cal-hol .hol-name { font-size: 10px; line-height: 15px; font-weight: 600; color: var(--ink);
  flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The legend shows the real markers, so it reuses them — laid out in a row
   rather than stacked full-width the way they sit inside a day cell. */
.cal-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin: 0 0 10px; font-size: 12px; color: var(--muted); }
.cal-legend .cal-chip, .cal-legend .cal-bday { display: inline-block; margin: 0;
  pointer-events: none; }
.cal-legend .cal-hol { display: inline-flex; margin: 0; }
.cal-legend .cal-hol .hol-name { flex: 0 0 auto; }
/* Country code chip (ENG / RSA / IND …) — flags render as letter pairs on Windows. */
tr.hol-past > td { opacity: .5; }
.cc { display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: .04em;
  padding: 0 4px; border-radius: 3px; background: var(--card-2); color: var(--muted);
  border: 1px solid var(--line); vertical-align: 1px; }
/* The same chip used as the in-cell public-holiday marker on the calendar. */
.cc.hol { background: color-mix(in srgb, var(--cal-hol) 34%, var(--card));
  border-color: color-mix(in srgb, var(--cal-hol) 75%, var(--line)); color: var(--ink);
  font-size: 9px; line-height: 13px; padding: 0 3px; vertical-align: baseline; }
.cc.hol.prov { border-style: dashed; }
@media (max-width: 600px) {
  .cal-cell { height: auto; min-height: 62px; padding: 4px; }
  .cal-chip { font-size: 10px; }
  table.cal { min-width: 560px; }
}

/* Inline segmented filter (e.g. format on Profit by competition). */
.fmt-filter { display: inline-flex; gap: 3px; flex-wrap: wrap; }
.fmt-filter a { font-size: 11.5px; font-weight: 600; padding: 3px 10px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); background: var(--card-2);
  text-transform: none; letter-spacing: 0; }
.fmt-filter a:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.fmt-filter a.on { background: var(--accent); border-color: var(--accent); color: #16233a; }

/* Rota load strip: matches per day, doubling as day navigation. */
.load-strip { display: flex; gap: 7px; align-items: stretch; overflow-x: auto; padding: 4px 0 2px; }
.ls-day { display: flex; flex-direction: column; align-items: center; gap: 3px;
  min-width: 36px; color: var(--muted); font-size: 10.5px; flex: 0 0 auto; }
.ls-day:hover { text-decoration: none; color: var(--accent); }
.ls-count { font-weight: 700; color: var(--ink); font-size: 11px; font-variant-numeric: tabular-nums; }
.ls-bar { height: 44px; width: 26px; background: var(--track); border-radius: 5px;
  display: flex; align-items: flex-end; overflow: hidden; }
.ls-bar > span { display: block; width: 100%; min-height: 2px; border-radius: 5px 5px 0 0;
  background: linear-gradient(180deg, var(--accent-soft), var(--accent)); }
.ls-lbl { text-align: center; line-height: 1.15; }
.ls-day.today .ls-lbl, .ls-day.today .ls-count { color: var(--accent); font-weight: 700; }

/* Long list tables: keep the column headers visible while scrolling. Desktop
   only — on phones these tables sit in overflow-x containers, which would
   trap the sticky positioning. Offset by the fixed top bar's height. */
@media (min-width: 601px) {
  #tp-table thead th, table.sticky-head thead th {
    position: sticky; top: 54px; z-index: 5;
    background: var(--card-2);        /* opaque: rows scroll underneath */
    box-shadow: 0 6px 12px -8px rgba(0, 0, 0, .35);
  }
}
.kpi .v { font-size: 26px; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -.02em; line-height: 1.1; }
.kpi .k { font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin-top: 4px; }
.kpi .l { font-size: 11px; margin-top: 2px; }

/* ---------- charts (theme-aware via classes) ---------- */
.chart-axis { stroke: var(--line-strong); }
.chart-grid { stroke: var(--line); }
.chart-track { fill: var(--track); }
.chart-fill { fill: var(--accent); }
.chart-line { fill: none; stroke: var(--accent); stroke-width: 2.5; }
.chart-dot { fill: var(--dot); }
.chart-label { fill: var(--muted); }
.chart-value-in { fill: #fff; }
.chart-value-out { fill: var(--ink); }

/* ---------- multi-series chart palette (theme-aware) ---------- */
:root {
  --ser1: #d3781f; --ser2: #2f9e5b; --ser3: #1d4468; --ser4: #c0392b;
  --ser5: #7a5ea8; --ser6: #2aa0a0;
}
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) {
  --ser1: #f0a355; --ser2: #46b979; --ser3: #5b9bd5; --ser4: #e0685b;
  --ser5: #a98fd0; --ser6: #47c4c4;
}}
:root[data-theme="dark"] {
  --ser1: #f0a355; --ser2: #46b979; --ser3: #5b9bd5; --ser4: #e0685b;
  --ser5: #a98fd0; --ser6: #47c4c4;
}
.cl-0{stroke:var(--ser1)} .cl-1{stroke:var(--ser2)} .cl-2{stroke:var(--ser3)}
.cl-3{stroke:var(--ser4)} .cl-4{stroke:var(--ser5)} .cl-5{stroke:var(--ser6)}
.cf-0{fill:var(--ser1)} .cf-1{fill:var(--ser2)} .cf-2{fill:var(--ser3)}
.cf-3{fill:var(--ser4)} .cf-4{fill:var(--ser5)} .cf-5{fill:var(--ser6)}
.c-grid{stroke:var(--line)} .c-axis{stroke:var(--line-strong)} .c-lbl{fill:var(--muted)}
.c-track{fill:var(--track)}
.c-val{fill:var(--ink)}            /* data labels on bars & lines */
.c-slice{fill:#fff;paint-order:stroke;stroke:rgba(16,38,60,.35);stroke-width:2.4px}  /* % on pie slices */
/* Ranked-bar category names are the content, not the scaffolding, so they read
   in full ink while the count axis behind them stays recessive. */
.hbar-name{fill:var(--ink)}
svg.chart{width:100%;height:auto;max-height:250px;display:block}

/* Brand banners (Decimal Data artwork — dark artwork, so a navy mat blends
   the edges on light theme and it sits flush on dark). */
.hero-banner{display:block;width:100%;border-radius:12px;overflow:hidden;
  background:var(--navy);margin-bottom:18px;
  box-shadow:0 1px 3px rgba(16,38,60,.14)}
.hero-banner img{display:block;width:100%;height:auto}
.hero-banner.slim img{max-height:120px;object-fit:cover;object-position:center}
.login-cover{display:block;width:100%;border-radius:12px 12px 0 0;overflow:hidden;
  background:var(--navy)}
.login-cover img{display:block;width:100%;height:auto}
.chart-legend{display:flex;flex-wrap:wrap;gap:6px 16px;margin-top:8px;font-size:12px;color:var(--muted)}
.chart-legend span{display:inline-flex;align-items:center;gap:6px}
.chart-legend i{width:11px;height:11px;border-radius:3px;display:inline-block}
/* Two-up when there are two charts; a lone chart fills the row (auto-fit
   collapses the empty track) instead of leaving half the card blank. */
.chart-grid-2{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:18px}
/* Wide single charts (e.g. the competency band) fill the card width rather than
   shrinking to the 250px height cap and stranding whitespace either side. */
.chart-fill svg.chart{max-height:380px}
/* Small/square charts sit centred so the spare width reads as deliberate. */
.chart-center{max-width:560px;margin:0 auto}

/* ---------- promotion criteria checklist ---------- */
input[type="checkbox"], input[type="radio"] { width: auto; }
.crit-list { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 24px; }
@media (max-width: 860px) { .crit-list { grid-template-columns: 1fr; } }
.crit-item {
  display: flex; gap: 11px; align-items: flex-start; padding: 8px 10px; margin: 0;
  border-radius: 8px; font-size: 13px; cursor: pointer; transition: background .12s ease;
  text-transform: none; letter-spacing: normal; color: var(--ink);
}
.crit-item:hover { background: var(--card-2); }
.crit-item.readonly { cursor: default; }
.crit-item.readonly:hover { background: transparent; }
.crit-item input { position: absolute; opacity: 0; width: 0; height: 0; }
.crit-box {
  position: relative; flex: 0 0 auto; width: 19px; height: 19px; border-radius: 6px;
  border: 1.5px solid var(--line-strong); background: var(--card);
  display: inline-flex; align-items: center; justify-content: center;
  margin-top: 1px; transition: background .12s ease, border-color .12s ease;
}
.crit-box svg { position: absolute; width: 12px; height: 12px; opacity: 0; transition: opacity .12s ease; stroke-width: 3; fill: none; stroke-linecap: round; }
/* met -> green check */
.crit-item input:checked + .crit-box { background: var(--good); border-color: var(--good); }
.crit-item input:checked + .crit-box .ic-check { opacity: 1; stroke: #fff; }
/* not met -> red cross */
.crit-item input:not(:checked) + .crit-box { background: color-mix(in srgb, var(--poor) 12%, var(--card)); border-color: var(--poor); }
.crit-item input:not(:checked) + .crit-box .ic-cross { opacity: 1; stroke: var(--poor); }
.crit-item input:focus-visible + .crit-box { box-shadow: var(--ring); }
.crit-text { color: var(--ink); line-height: 1.5; }
.crit-item input:not(:checked) ~ .crit-text { color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* ---------- Training programme ---------- */
.tr-mod { border: 1px solid var(--line); border-radius: var(--r); margin-bottom: 10px; overflow: hidden; }
.tr-mod > summary { list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 10px; padding: 10px 14px; background: var(--card-2); }
.tr-mod > summary::-webkit-details-marker { display: none; }
.tr-mod > summary::before { content: "▸"; color: var(--muted); font-size: 11px;
  transition: transform .15s ease; flex: 0 0 auto; }
.tr-mod[open] > summary::before { transform: rotate(90deg); }
.tr-mod > summary:hover { background: color-mix(in srgb, var(--accent) 8%, var(--card-2)); }
.tr-modnum { font-weight: 800; color: var(--accent); font-variant-numeric: tabular-nums; min-width: 18px; }
.tr-modttl { font-weight: 700; }
.tr-prog { margin-left: auto; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tr-prog.ok { color: var(--good); font-weight: 700; }
.tr-body { padding: 6px 14px 14px; }
.tr-group-h { font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--accent); font-weight: 700; margin: 13px 0 2px; }
.tr-head, .tr-row { display: grid; grid-template-columns: minmax(0,1fr) 132px 92px minmax(0,1.1fr);
  gap: 8px 12px; align-items: center; }
.tr-head { font-size: 10px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); font-weight: 700; padding: 6px 0 2px; }
.tr-row { padding: 7px 0; border-top: 1px solid var(--line); }
.tr-tick { display: flex; gap: 9px; align-items: flex-start; cursor: pointer; margin: 0; }
.tr-tick input { margin-top: 3px; flex: 0 0 auto; }
.tr-text { font-size: 13.5px; line-height: 1.4; color: var(--ink);
  text-transform: none; letter-spacing: normal; }
.tr-row.done .tr-text { color: var(--muted); }
.tr-body input[type=date], .tr-body input[type=text] { padding: 5px 7px; font-size: 12.5px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--card-2); color: var(--ink); width: 100%; }
@media (max-width: 700px) {
  .tr-head { display: none; }
  .tr-row { grid-template-columns: 1fr 1fr; gap: 6px 10px; }
  .tr-tick { grid-column: 1 / -1; }
  .tr-notes { grid-column: 1 / -1; }
}

/* ============================================================
   Mobile refinements
   Everything below lives inside a max-width media query, so the
   desktop layout is unchanged. Goals: use the narrow width, keep
   wide tables from pushing the whole page sideways, and let the
   top bar and hero adapt instead of overflowing.
   ============================================================ */

/* Tablet / large phone */
@media (max-width: 720px) {
  .wrap { padding: var(--s4) var(--s3); }
}

/* Phones */
@media (max-width: 600px) {
  .wrap { padding: var(--s3); }

  /* Top bar wraps to a second line rather than overflowing. Dropdown
     menus keep working because no ancestor gets an overflow clip. */
  header.topbar {
    height: auto; min-height: 50px; flex-wrap: wrap;
    padding-top: 6px; padding-bottom: 6px; row-gap: 4px;
  }
  header.topbar nav { flex-wrap: wrap; justify-content: flex-end; gap: 2px 4px; }

  /* Wide tables scroll inside their card instead of stretching the page.
     Nothing here changes how they look on a wider screen. */
  .card { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .month-group { overflow-x: auto; }
  table { font-size: 13px; }
  th, td { padding: 8px 9px; }

  /* Trader-review hero stacks and centres. */
  .hero { flex-direction: column; text-align: center; gap: var(--s4); padding: var(--s5) var(--s4); }
  .hero .meta { min-width: 0; }
  .hero .meta .name, .hero .peer-chips { justify-content: center; }
  .hero .score { font-size: 46px; }

  /* Headline numbers a touch smaller so single-column tiles never clip. */
  .kpi .v { font-size: 23px; }

  /* Search takes its own full row above the filter/sort selects. */
  .list-controls #tp-search { min-width: 0; flex: 1 0 100%; }
}

/* Rota schedule: a card per match on phones so it fits without side-scrolling.
   The wide table stays the desktop view; the cards are hidden until <=600px. */
.rota-cards { display: none; }
@media (max-width: 600px) {
  .rota-day .rota-wide { display: none; }
  .rota-cards { display: flex; flex-direction: column; gap: 10px; }
  .rota-card {
    background: var(--card-2); border: 1px solid var(--line);
    border-radius: var(--r); padding: 10px 12px;
  }
  .rota-card .rc-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .rota-card .rc-time { font-weight: 700; font-variant-numeric: tabular-nums; }
  .rota-card .rc-cat { font-size: 11px; color: var(--muted); }
  .rota-card .rc-pnl { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
  .rota-card .rc-match { font-weight: 700; font-size: 15px; margin-top: 5px; }
  .rota-card .rc-match .v { color: var(--muted); font-weight: 400; }
  .rota-card .rc-sub { color: var(--muted); font-size: 12.5px; margin-top: 1px; }
  .rota-card .rc-people { margin-top: 8px; }
  .rota-card .rc-lbl {
    display: block; font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
    color: var(--muted); margin-bottom: 4px;
  }
  /* chips already wrap; give them a little breathing room in the card */
  .rota-card .rota-chip { margin: 0 4px 4px 0; }
}

/* Utility: hide a low-priority table column (or anything) on phones only. */
@media (max-width: 600px) { .hide-sm { display: none !important; } }

/* Wide matrix tables: pin the first column while the rest scrolls, so you
   always know which row you're reading (e.g. Data completeness). */
@media (max-width: 600px) {
  table.sticky-first th:first-child, table.sticky-first td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--card);
    box-shadow: 1px 0 0 var(--line);
  }
  table.sticky-first thead th:first-child { background: var(--card-2); }
}

/* Generic reflow: stack a table into labelled cards on phones. Opt in with
   class="mreflow"; every <td> supplies a data-label. A cell marked .mfull
   puts its label above a full-width value (for long text like a path). */
@media (max-width: 600px) {
  table.mreflow thead { display: none; }
  table.mreflow, table.mreflow tbody, table.mreflow tr, table.mreflow td { display: block; width: 100%; }
  table.mreflow tr {
    border: 1px solid var(--line); border-radius: var(--r);
    background: var(--card-2); padding: 4px 12px; margin-bottom: 10px;
  }
  table.mreflow td {
    border: 0 !important; padding: 5px 0; text-align: right;
    display: flex; justify-content: space-between; gap: 14px; align-items: baseline;
  }
  table.mreflow td::before {
    content: attr(data-label); text-align: left; flex: 0 0 auto; color: var(--muted);
    font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; font-weight: 700;
  }
  table.mreflow td.mfull { display: block; text-align: left; }
  table.mreflow td.mfull::before { display: block; margin-bottom: 2px; }
}

/* ---- Screenshot on a suggestion ---------------------------------------- */
.shotzone {
  border: 1px dashed var(--line);
  border-radius: 8px;
  padding: 14px;
  text-align: center;
  background: var(--accent-weak);
  font-size: 13px;
}
.shotzone.over { border-color: var(--accent); background: var(--card); }
.shotzone kbd {
  font: 600 11px ui-monospace, Menlo, Consolas, monospace;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 1px 5px;
  background: var(--card);
}
.shotzone img {
  max-width: 100%;
  max-height: 320px;
  border: 1px solid var(--line);
  border-radius: 6px;
}
/* A button that reads as a link — the pick/remove actions must not look like
   submit buttons sitting inside a form. */
.linkish {
  background: none; border: 0; padding: 0;
  color: var(--accent); font: inherit; text-decoration: underline; cursor: pointer;
}
.shot-thumb {
  display: block;
  margin-top: 6px;
  max-width: 100%;
  max-height: 220px;
  border: 1px solid var(--line);
  border-radius: 6px;
}
.shotsteps {
  margin: 0 0 10px;
  padding-left: 20px;
  text-align: left;
  font-size: 12.5px;
  line-height: 1.75;
}
.shotsteps li { padding-left: 2px; }
/* A real editable field, so the browser offers Paste on right-click — the
   whole point of it. Sized to look like a target, not a comment box. */
.shotpaste {
  width: 100%;
  resize: none;
  text-align: center;
  padding: 12px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
}
.shotpaste:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.shotpaste::placeholder { color: var(--muted); }

/* ---- Opportunities, once per comparison group -------------------------- */
.lever-view + .lever-view {
  border-top: 1px solid var(--line);
  margin-top: 14px;
  padding-top: 12px;
}
.lever-view-head {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
  margin-bottom: 8px;
}

/* ---- Inline edit of a free-text log row --------------------------------- */
.log-edit-row > td {
  background: var(--card-2);
  padding: 12px 10px;
}
.log-edit-row .entry-form { margin: 0; }

/* ---- Integrity register: severity pills -------------------------------- */
.sev {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  white-space: nowrap;
}
/* The grade is a colour, so the pill is that colour. Always beside its name —
   the word carries the meaning, the colour only makes it findable. */
.sev-Green { color: var(--good);
             border-color: color-mix(in srgb, var(--good) 45%, var(--line));
             background: color-mix(in srgb, var(--good) 10%, transparent); }
.sev-Amber { color: var(--accent);
             border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
             background: var(--accent-weak); }
.sev-Red   { color: var(--poor);
             border-color: color-mix(in srgb, var(--poor) 55%, var(--line));
             background: color-mix(in srgb, var(--poor) 12%, transparent); }
.sev-Grey  { color: var(--muted); }
.sev-none  { color: var(--muted); border-style: dashed; }

/* ---- Integrity register: collapsed details ----------------------------- */
/* The notes run to several thousand characters, which buries every other
   column. Collapsed to a first line by default, opened with a click —
   <details> so it works without JavaScript and stays keyboard-reachable. */
/* Ten columns in a 1040px card: every free-text column is capped and wraps,
   so the table fits instead of scrolling sideways inside its own box. */
/* Free-text columns are capped so one long competition name or note can't
   dominate; the table's own wrapper scrolls if it still needs to, which is how
   every other table in the app behaves. */
td.det  { max-width: 210px; min-width: 170px; }
td.comp { max-width: 150px; }
td.tm   { max-width: 110px; }
td.plyr { max-width: 130px; }
td.by   { max-width: 100px; }
/* Two attribution columns now sit side by side — who recorded it and who
   signed it off — so the details column gives up the room they need. */
td.det  { max-width: 190px; }
/* Nine columns in a 1042px card: shave the cell padding so the last column
   isn't clipped, rather than dropping a column the desk asked for. */
table td.det, table td.comp, table td.tm, table td.plyr, table td.by,
table th { padding-left: 8px; padding-right: 8px; }
.det-collapse summary {
  cursor: pointer;
  color: var(--muted);
  /* Inside, not outside: the summary clips its overflow to stay on one line,
     and an outside marker gets clipped with it — leaving no cue that the row
     opens at all. */
  list-style-position: inside;
}
.det-collapse summary:hover { color: var(--ink); }
.det-collapse[open] summary {
  color: var(--muted);
  margin-bottom: 6px;
}
.det-full { white-space: pre-wrap; }

/* The nav is full at eleven items. Rather than let it push the page sideways on
   a narrower screen, tighten its spacing only once space actually runs short —
   the layout at normal widths is untouched.

   These are the last word on the bar's spacing: they sit at the end of the file
   and so beat the base rule and anything written next to it. Put a new
   breakpoint here rather than up beside the topbar block, or it will lose to
   this one and look as though it simply had no effect. */
@media (max-width: 1400px) {
  header.topbar { padding: 0 var(--s3); gap: 10px; }
  header.topbar nav > a,
  header.topbar .menu-btn { padding: 6px 7px; }
  .topsearch { margin: 0 4px 0 2px; }
}
@media (max-width: 1150px) {
  header.topbar nav { gap: 1px; }
  header.topbar nav > a,
  header.topbar .menu-btn { padding: 6px 5px; font-size: 12.5px; }
}
@media (max-width: 1100px) {
  /* The mark shrinks rather than losing a word. It used to drop the second
     one, which was fine when that left "TRADER" but leaves "DECIMAL" now —
     half a logo, and the half that doesn't say which app this is. */
  header.topbar .dp-brand { height: 18px !important; }
}

/* The count chips are the severity filter. */
.chip-link { text-decoration: none; cursor: pointer; }
.chip-link:hover { border-color: var(--line-strong); text-decoration: none; }
.chip-link.on {
  background: var(--accent-weak);
  border-color: color-mix(in srgb, var(--accent) 50%, var(--line));
  color: var(--ink);
}
.sev-chip-Red.on   { border-color: color-mix(in srgb, var(--poor) 55%, var(--line)); }
.sev-chip-Green.on { border-color: color-mix(in srgb, var(--good) 50%, var(--line)); }

/* One line only, whatever the column width — the note opens if you want it.
   Clamped on the summary itself rather than on a flex child, so the browser's
   disclosure triangle survives: without it there is no cue that it opens. */
.det-collapse summary {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.det-collapse[open] summary { text-overflow: clip; }

/* A page someone may read but not write to. The forms that post are hidden
   rather than disabled: a greyed-out entry form still reads as "you could do
   this", and they cannot. Filter forms use GET and are unaffected. */
body.view-only form[method="post"],
body.view-only form:not([method]) { display: none; }
body.view-only .view-only-note { display: block; }
.view-only-note { display: none; }

/* Cycle Goals — sits in the hero, so it borrows the hero's light-on-dark
   palette rather than the page's surface colours. */
.cycle-goals { margin-top: 12px; max-width: 640px; }
.cycle-goals details > summary {
  list-style: none; cursor: pointer; display: flex; gap: 8px;
  align-items: baseline; padding: 6px 10px; border-radius: 6px;
  background: rgba(255, 255, 255, .07);
}
.cycle-goals details > summary::-webkit-details-marker { display: none; }
.cycle-goals details > summary:hover { background: rgba(255, 255, 255, .12); }
.cycle-goals details[open] > summary { border-radius: 6px 6px 0 0; }
.cg-label {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: rgba(255, 255, 255, .62); white-space: nowrap;
}
.cg-peek {
  font-size: 12.5px; color: rgba(255, 255, 255, .92); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.cg-empty { color: rgba(255, 255, 255, .5); font-style: italic; }
.cg-form {
  background: rgba(255, 255, 255, .07); border-radius: 0 0 6px 6px;
  padding: 10px; margin: 0;
}
.cg-form textarea {
  width: 100%; font: inherit; font-size: 13px; resize: vertical;
  background: rgba(0, 0, 0, .25); color: #fff; border-radius: 5px;
  border: 1px solid rgba(255, 255, 255, .2); padding: 8px;
}
.cg-actions { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.cg-meta { font-size: 11px; color: rgba(255, 255, 255, .55); }
.cg-readonly {
  display: flex; gap: 8px; align-items: baseline; padding: 6px 10px;
  border-radius: 6px; background: rgba(255, 255, 255, .07);
}
/* On the printed one-pager the goals are context worth keeping, but the form
   around them is not. */
@media print {
  .cycle-goals details > summary { background: none; padding: 0; }
  .cg-form, .cg-actions { display: none; }
  .cg-label, .cg-peek { color: #16202c; }
  .cg-peek { white-space: normal; }
}

/* ---------- Trader history timeline ---------- */
.tl { list-style: none; margin: 0; padding: 0; }
.tl-row { display: flex; gap: 12px; align-items: flex-start; padding: 9px 4px;
  border-bottom: 1px solid var(--line); }
.tl-row:last-child { border-bottom: 0; }
.tl-when { flex: none; width: 74px; font-size: 12px; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--ink); }
.tl-when span { display: block; font-weight: normal; font-size: 11px; }
.tl-body { flex: 1; min-width: 0; }
.tl-title { font-size: 13.5px; font-weight: 600; }
.tl-detail { font-size: 12.5px; color: var(--muted); margin-top: 2px;
  overflow-wrap: break-word; }
.tl-act { flex: none; }
/* A stripe per source, so a run of one kind reads as a block and the eye can
   skip to the manager notes without reading every line. */
.tl-row { border-left: 3px solid transparent; padding-left: 9px; }
.tl-row.src-note { border-left-color: var(--accent); background: var(--accent-weak); }
.tl-row.src-error { border-left-color: var(--poor); }
.tl-row.src-monitoring { border-left-color: var(--line-strong); }
.tl-row.src-score.tone-good { border-left-color: var(--good); }
.tl-row.src-score.tone-bad { border-left-color: var(--poor); }
.pill.src-on { border-color: var(--accent); color: var(--accent); font-weight: 700; }

/* ---------- Search box in the top bar ---------- */
.topsearch { margin: 0 8px 0 4px; display: flex; }
.topsearch input {
  width: 150px; padding: 5px 10px; font-size: 13px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--card); color: inherit;
  transition: width .14s ease, border-color .14s ease;
}
.topsearch input:focus { width: 230px; border-color: var(--accent); outline: none; }
/* Narrower where the bar is tight, but never gone: it still takes a search, and
   it grows back to full width the moment it has focus. */
@media (max-width: 1400px) { .topsearch input { width: 112px; } }
@media (max-width: 900px) { .topsearch { display: none; } }

/* ---------- Empty states ----------
   Given room and a centre line, because an empty page is where somebody is
   least sure whether they have done something wrong. The mark is a hairline
   ring rather than an illustration: this is a work tool, and a drawing here
   would be louder than anything on a page that has data in it. */
.empty { text-align: center; padding: var(--s6) var(--s4); }
.empty-mark {
  width: 40px; height: 40px; margin: 0 auto var(--s3);
  display: flex; align-items: center; justify-content: center;
  border: 1px dashed var(--line-strong); border-radius: 50%;
  color: var(--muted); font-size: var(--fs-md); line-height: 1;
}
.empty-title { margin: 0; font-weight: 600; color: var(--ink); font-size: var(--fs-base); }
.empty-hint {
  margin: var(--s1) auto 0; max-width: 46ch; color: var(--muted);
  font-size: var(--fs-sm); line-height: 1.55;
}
.empty-action { margin-top: var(--s3); }
/* Inside a card that is only part of a page, the state is a note rather than a
   destination — it should not push the rest of the page down. */
.card .empty.compact { padding: var(--s4) var(--s3); }
.card .empty.compact .empty-mark { width: 30px; height: 30px; margin-bottom: var(--s2); }

/* ── Layout utilities ──────────────────────────────────────────────────────
   These five declarations were written inline 193 times between them. Inline
   styles win against every selector in this file, so while they sat in the
   markup any rule here that touched the same property was quietly losing.
   Pulling them out is only safe where the class can still win, which is the
   whole reason .card-head is written as a compound selector below.          */

.scroll-x { overflow-x: auto; }
.nowrap { white-space: nowrap; }
.inline { display: inline; }

/* The subtitle under a card's heading: the heading already carries a bottom
   border and padding, so the line beneath it is pulled back up against it. */
.card-sub { margin-top: -4px; }

/* .card h2 (line ~442) already sets display:flex, align-items and gap:8px at
   specificity (0,1,1). A bare .card-head is (0,1,0) and would LOSE to it —
   the gap would snap back to 8px and, far worse, flex-wrap would vanish, so a
   long heading could no longer wrap and would run out past the card's edge.
   Hence the compound selector: (0,2,1) beats it. The bare class is kept for
   the handful of headers that sit outside a .card. */
.card-head { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.card h2.card-head { gap: 10px; flex-wrap: wrap; }


/* --- Files attached to a log row -------------------------------------------
   These sit in log sections that range from two columns to eight, so the
   Files column has to take the least room it can: at eight columns a greedy
   one pushed the Edit/delete buttons off the card entirely.

   Each chip is one line, truncated, with the full name on hover. The name is
   in its own span because text-overflow needs an element to act on — as a bare
   text node it wrapped mid-filename and stretched the column instead. */
/* The strip of attachments under a log row. Not a column of its own: these
   sections range from two fields to eight, and a fixed Files column that sat
   well on Catch-ups pushed the row buttons off the card on Competition
   Leadership. Spanning the row costs the table no width in any of them. */
.files-row > td { border-top: 0; padding-top: 0; padding-bottom: 10px; }
.files-label {
  margin-right: 8px; color: var(--muted);
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
}
.filechip {
  display: inline-flex; align-items: center; gap: 5px;
  max-width: 240px; margin: 2px 4px 2px 0; padding: 2px 9px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--card-2); color: var(--link);
  font-size: var(--fs-xs); text-decoration: none; vertical-align: middle;
}
.filechip:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.filechip .clip { flex: none; opacity: .75; }
.filechip .fn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* In the edit panel the chip carries its own link and a size, so there it is a
   plain container rather than the link itself. */
.filechip.static { max-width: none; color: var(--muted); gap: 7px; }
.filechip.static a { display: inline-flex; align-items: center; gap: 5px; max-width: 220px; color: var(--link); }
.filechip.static a:hover { color: var(--accent); text-decoration: none; }
.attached-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.attached-list .inline { display: inline-flex; margin: 0; }

/* The upload control. Left native, so the browser still names the chosen
   files, but the button it draws is brought into line with the app's own —
   the default grey system button beside these forms was the single thing that
   made the whole feature read as bolted on. */
.attach-field { margin-top: 2px; }
.attach-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.attach-row input[type="file"] {
  width: auto; flex: 1 1 260px; min-width: 0;
  padding: 5px 8px; font-size: var(--fs-sm); color: var(--muted);
  background: var(--card); border: 1px dashed var(--line-strong); border-radius: var(--r-sm);
}
.attach-row input[type="file"]::file-selector-button {
  margin-right: 10px; padding: 6px 13px;
  background: transparent; color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  font: inherit; font-size: 13px; font-weight: 700; letter-spacing: .01em;
  cursor: pointer; transition: background .15s ease, border-color .15s ease;
}
.attach-row input[type="file"]::file-selector-button:hover {
  background: var(--card-2); border-color: var(--accent); color: var(--accent);
}
.attach-hint { flex: 1 1 220px; font-size: var(--fs-xs); line-height: 1.45; }

/* The `hidden` attribute has to actually hide.
   It only carries `display: none` from the browser's own stylesheet, which any
   author rule outranks — so every component here that sets a display (.pill,
   .list-controls, .flag, .chip-row …) has been quietly ignoring it. On the rota
   alerts page that meant "✓ all clear" sitting next to "2 to fix", and a
   "0 to check" pill that should not have been on screen at all. Global, and
   !important, because the point is to beat the component rule whichever one it
   turns out to be. */
[hidden] { display: none !important; }

/* --- Reading the schedule on another clock ---------------------------------
   The rota is written in UK time. When it is shown in someone else's, the
   column says so and any fixture that lands on a different date there carries
   the offset — "01:00" under a Tuesday heading is a worse answer than leaving
   the time alone. */
.tz-head {
  margin-left: 5px; padding: 1px 6px; border-radius: 999px;
  background: var(--accent-weak); color: var(--accent);
  font-size: var(--fs-micro); font-weight: 700; letter-spacing: .04em;
  text-transform: none; white-space: nowrap;
}
.tz-shift {
  margin-left: 3px; padding: 0 4px; border-radius: 4px;
  background: var(--accent-weak); color: var(--accent);
  font-size: var(--fs-micro); font-weight: 700; font-variant-numeric: tabular-nums;
}

/* What a logged request altered, under the record it altered. Quieter than the
   name above it: the name answers "which one", this answers "what about it",
   and the second only matters once the first has been read. */
.audit-changes {
  margin-top: 3px; color: var(--muted);
  font-size: var(--fs-micro); line-height: 1.45;
  font-variant-numeric: tabular-nums;
  /* One row of the database per line. A request that edits fifty traders at
     once is the case this exists for, and running those together would put
     the reader back where they started. */
  white-space: pre-line;
  max-width: 46em;
}

/* ---------- restoring from a backup ----------
   Folded away behind a summary and styled apart from the buttons above it.
   Taking a copy is routine; replacing every row in the database with one is
   not, and the page should not offer them as a matched pair. */
.restore-box {
  margin-top: 14px; border: 1px solid var(--line-strong);
  border-radius: var(--r-sm); background: var(--card-2);
}
.restore-box > summary {
  cursor: pointer; padding: 9px 12px; font-size: var(--fs-sm); font-weight: 700;
  color: var(--muted); list-style: none;
}
.restore-box > summary::-webkit-details-marker { display: none; }
.restore-box > summary::before { content: "▸ "; }
.restore-box[open] > summary::before { content: "▾ "; }
.restore-box[open] > summary { color: var(--ink); border-bottom: 1px solid var(--line); }
.restore-box > *:not(summary) { margin-left: 12px; margin-right: 12px; }
.restore-box > *:last-child { margin-bottom: 12px; }

.restore-form {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end;
  margin-top: 10px;
}
.restore-field { display: flex; flex-direction: column; gap: 4px; flex: 1 1 220px; }
.restore-field > span { font-size: var(--fs-xs); color: var(--muted); }

.btn.danger { background: var(--poor); }

/* What the last attempt did. Said in the page rather than an alert, so it can
   be read twice and copied — it names the file the old database was kept in. */
.restore-said {
  margin-top: 10px; padding: 9px 11px; border-radius: var(--r-sm);
  font-size: var(--fs-sm); line-height: 1.45;
}
.restore-said.ok  { border-left: 3px solid var(--good); background: rgba(47,158,91,.10); }
.restore-said.bad { border-left: 3px solid var(--poor); background: rgba(192,57,43,.10); }

/* A row older than the change-detail column. Its blank Detail cell would
   otherwise read exactly like an action that genuinely changed nothing, and
   a reader cannot tell those apart — which reads as a broken feature. */
.audit-changes.none { opacity: .55; font-style: italic; }

/* ---------- the wordmark, on whichever ground the viewer has ----------
   The type in the artwork is a solid colour, so there is one version for a
   light ground and one for dark. The sign-in card used to hardcode the light
   one; in dark mode that is dark type on a dark card, which is what the desk
   reported as "you can barely read the logo".

   Three states to cover, not two: an explicit choice stamps data-theme, and
   the default "follow the system" stamps nothing — so the media query has to
   carry that case on its own, guarded so an explicit light choice still wins
   on a dark OS. */
.dp-on-light { display: block; }
.dp-on-dark  { display: none; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .dp-on-light { display: none; }
  :root:not([data-theme="light"]) .dp-on-dark  { display: block; }
}
:root[data-theme="dark"] .dp-on-light { display: none; }
:root[data-theme="dark"] .dp-on-dark  { display: block; }

/* Centred in the sign-in card, where the macro is used. */
.dp-on-light, .dp-on-dark { margin-inline: auto; }

/* When the description is the only thing naming what happened, it is the
   Detail — not a footnote under a URL. The path it was posted to stays on the
   hover: it is how the row was recorded, but it is not what anybody came to
   the page to read. */
.audit-changes.lead {
  margin-top: 0;
  color: var(--ink);
  font-size: var(--fs-xs);
}
/* The fallback, for rows old enough to have nothing else. Kept quiet so it
   does not read as the answer. */
.audit-path { opacity: .7; }
