/* ==========================================================================
   KSH Group - design tokens for the team performance system
   --------------------------------------------------------------------------
   Palette: #0969da primary (the same blue as the existing admin login button,
   so the two apps look like one product), white surfaces, charcoal accents.

   Every colour below was measured, not eyeballed:
     #0969da  5.19:1 on white   primary - passes text AND mark
     #0550ae  7.59:1            hover
     #24292f 14.65:1            charcoal
     #1a7f37  5.08:1            done
     #9a6700  4.87:1            due soon
     #cf222e  5.36:1            overdue (same red the login page already uses)
     #6e7781  4.55:1            the lightest grey that still passes as text
   #7d8590 was rejected as muted text - 3.73:1, fails.

   TWO RULES
   ---------
   1. Primary blue is CHROME ONLY - buttons, active nav, links, focus rings.
      It is never a status and never a chart series. The series palette below
      therefore contains NO blue, so nothing can impersonate a button.
   2. Status colours appear ONLY as tinted pills carrying an icon AND a word
      ("Overdue", "Due soon", "Done"). Never a solid fill, never colour alone -
      that keeps them readable for colour-blind users and in print.

   Series palette passed all six dataviz checks against a #ffffff surface:
   worst adjacent CVD dE 9.1, worst adjacent normal-vision dE 19.6.
   Light mode only, by brand direction: a dark theme needs its own validated
   pass, not an automatic flip.
   ========================================================================== */

:root {
  /* ---- primary (chrome only) ---- */
  --primary:        #0969da;
  --primary-hover:  #0550ae;
  --primary-active: #033d8b;
  --primary-tint:   #ddf4ff;   /* wash only - never text on white */
  --primary-ring:   #0969da66;

  /* ---- charcoal (the "some charcoal here and there") ---- */
  --charcoal:       #24292f;
  --charcoal-soft:  #424a53;
  --charcoal-tint:  #f1f3f5;

  /* ---- surfaces & ink: white, separated by hairlines not grey fills ---- */
  --surface:        #ffffff;
  --border:         #d8dce1;
  --border-strong:  #b9c0c8;
  --ink:            #1f2328;
  --ink-secondary:  #57606a;
  --ink-muted:      #6e7781;

  /* ---- task status: PILLS ONLY, always icon + label ----
     Eight statuses, one hue each, measured not eyeballed. Two numbers matter
     and only one of them is obvious:

       fg vs its OWN TINT   >= 4.5   the pill is coloured text on a tint, so
                                     this - not fg-vs-white - is the contrast a
                                     reader actually gets. Measuring against
                                     white flatters every one of these by
                                     roughly 0.6, which is how "Due today" and
                                     "To do" previously shipped at 4.43 and
                                     4.27 while looking compliant on paper.
       tint vs WHITE        >= 1.10  or the pill has no visible edge at all.

     Worst adjacent pair is Overdue vs On hold at dE 16.4 (want >= 11), and
     every status clears both chrome colours by dE >= 15 so none can be read
     as a button.                                                            */
  --done:      #146c2e;  --done-tint:      #e7f5ec;   /* 5.81 on tint */
  --duesoon:   #875d00;  --duesoon-tint:   #fcf0d8;   /* 5.17 */
  --overdue:   #cf222e;  --overdue-tint:   #fdecee;   /* 4.69 */
  --progress:  #0f6e6e;  --progress-tint:  #ddf3f2;   /* 5.23 - teal, in progress */
  --review:    #6639ba;  --review-tint:    #f0e9fd;   /* 6.22 - violet, awaiting sign-off */
  --hold:      #a84400;  --hold-tint:      #ffefe0;   /* 5.35 - rust, stopped */
  --rework:    #a2215e;  --rework-tint:    #ffe9f1;   /* 6.23 - magenta, sent back */
  --upcoming:  #5a636d;  --upcoming-tint:  #eef1f4;   /* 5.38 - grey, to do */

  /* ---- attendance board ---- */
  --in-office:   #1a7f37;
  --remote:      #424a53;
  --late:        #9a6700;
  --absent:      #cf222e;
  --unmonitored: #6e7781;   /* a dead agent is NOT an absence */

  /* ---- categorical series: no blue, by design ---- */
  --series-1: #eb6834;  /* orange  */
  --series-2: #1baf7a;  /* aqua    */
  --series-3: #eda100;  /* yellow  */
  --series-4: #e87ba4;  /* magenta */
  --series-5: #008300;  /* green   */
  --series-6: #4a3aa7;  /* violet  */
  /* series 2, 3 and 4 sit below 3:1 on white -> they REQUIRE visible direct
     labels or a table view. That is the relief rule, not a suggestion.      */

  /* ---- sequential (magnitude): one hue, light -> dark ---- */
  --seq-1: #ddf4ff; --seq-2: #b6e3ff; --seq-3: #80ccff; --seq-4: #54aeff;
  --seq-5: #218bff; --seq-6: #0969da; --seq-7: #0550ae; --seq-8: #033d8b;
  --seq-9: #0a3069; --seq-10: #002155;
  /* Ordinal use (discrete tiers) must start no lighter than --seq-3. */

  /* ---- chart chrome ---- */
  --grid: #eceff2;
  --axis: #c9ced4;

  /* ---- type & metrics ---- */
  --font: "Inter var", Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --r-sm: 6px; --r-md: 9px; --r-lg: 14px; --r-xl: 18px; --r-full: 999px;
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px;
  --sp-6: 32px; --sp-7: 44px;

  /* Layered rather than a single hairline drop. One soft ambient shadow plus a
     tighter contact shadow is what makes a white card read as sitting ON the
     canvas instead of being a box drawn on it. */
  --shadow-card: 0 1px 2px rgba(31,35,40,.04), 0 4px 16px rgba(31,35,40,.045);
  --shadow-pop:  0 4px 12px rgba(31,35,40,.08), 0 16px 40px rgba(31,35,40,.10);

  /* ---- the canvas ----------------------------------------------------
     THE ONE CHANGE EVERYTHING ELSE RESTS ON. The page is a soft tint and the
     cards stay #ffffff, so a card is visible because it is lighter than what
     is behind it - not because it has a border drawn round it.

     This is also why the surface stays pure white and did not become the tint:
     every status pill in this file was measured against #ffffff, and moving
     the surface would silently invalidate all eight contrast figures above.
     The canvas is chrome; nothing is ever read against it.                */
  --canvas:      #f6f8fb;
  --canvas-deep: #eef1f6;   /* sidebar, table heads, inset wells */

  --sidebar-w: 236px;
  --topbar-h: 60px;
}

/* ============================ primitives ============================ */

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
}

/* Headings tighten as they grow. A 26px line at the same tracking as body text
   reads loose and unresolved; negative tracking is what makes a large label
   look deliberate rather than merely big. */
h1, h2, h3 { margin: 0 0 var(--sp-3); line-height: 1.25; font-weight: 600; }
h1 { font-size: 26px; letter-spacing: -.021em; margin-bottom: var(--sp-5); }
h2 { font-size: 16px; letter-spacing: -.011em; }
h3 { font-size: 13px; }

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

:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.muted { color: var(--ink-muted); }
.secondary { color: var(--ink-secondary); }
.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---- app frame ---------------------------------------------------------
   A PERMANENT LEFT RAIL, not a row of tabs across the top.

   Twelve destinations is too many for a horizontal bar - they were already
   overflowing into a scroll on a laptop, which hides exactly the admin screens
   people go looking for. A rail gives every one of them a fixed home, room for
   an icon, and space to be GROUPED, so "Assign" sits with the work and
   "People" sits with administration instead of all twelve reading as one
   undifferentiated list.

   The rail scrolls independently of the page, so the content column can be as
   long as it likes without the navigation disappearing above it.           */
.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-height: 100vh;
}

.sidebar {
  position: sticky; top: 0; align-self: start;
  height: 100vh; overflow-y: auto;
  display: flex; flex-direction: column;
  padding: var(--sp-4) var(--sp-3) var(--sp-4);
  background: var(--canvas-deep);
  border-right: 1px solid var(--border);
}
.sidebar .brand {
  display: flex; align-items: center; gap: 10px;
  padding: var(--sp-2) var(--sp-3) var(--sp-5);
}
.sidebar .brand img { height: 24px; width: auto; display: block; }

.nav-group { margin-bottom: var(--sp-4); }
/* --ink-secondary, NOT --ink-muted. Muted grey is 4.55:1 on white but only
   4.02:1 on this deeper tint, which fails AA - and these are 10px uppercase,
   the least forgiving text in the app. A colour that passes on one surface does
   not pass on every surface. */
.nav-group > .label {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  color: var(--ink-secondary); padding: 0 var(--sp-3) 6px; margin: 0;
}
.nav a {
  display: flex; align-items: center; gap: 10px;
  padding: 8px var(--sp-3); margin-bottom: 1px;
  border-radius: var(--r-md);
  font-size: 13.5px; font-weight: 500; color: var(--ink-secondary);
  transition: background .12s, color .12s;
}
.nav a svg { flex: 0 0 17px; width: 17px; height: 17px; opacity: .78; }
.nav a:hover { background: rgba(31,35,40,.05); color: var(--ink); text-decoration: none; }
/* Three cues on the current page - fill, weight and colour - so it survives
   both a colour-blind reader and a monochrome print. */
.nav a[aria-current="page"] {
  background: var(--surface); color: var(--primary); font-weight: 600;
  box-shadow: var(--shadow-card);
}
.nav a[aria-current="page"] svg { opacity: 1; }
.nav .count {
  margin-left: auto; font-size: 11px; font-weight: 600;
  padding: 1px 7px; border-radius: var(--r-full);
  background: var(--charcoal-tint); color: var(--ink-secondary);
}

/* ---- the collapsing rail -------------------------------------------------
   The sidebar narrows to icons and back, and REMEMBERS which. On a 13" laptop
   eleven screens plus a wide table is a squeeze, and the rail buys back 168px
   without hiding navigation behind a hamburger - which the responsive rule
   further down already refuses to do, for the reason stated there.

   ABOVE 960px ONLY. Below that the rail is already a horizontal strip and
   "collapsed" has no meaning; applying it there would strip the labels from
   the one layout with room to spare.

   The width is a custom property on .app rather than a class on .sidebar, so
   the grid column and the padding move together off one number. */
@media (min-width: 961px) {
  .app { transition: grid-template-columns .18s ease; }
  .app[data-rail="1"] { --sidebar-w: 68px; }
  .app[data-rail="1"] .sidebar { padding-left: var(--sp-2); padding-right: var(--sp-2); }
  .app[data-rail="1"] .brand { justify-content: center; padding: var(--sp-2) 0 var(--sp-5); }
  .app[data-rail="1"] .nav a { justify-content: center; gap: 0;
                               padding-left: 0; padding-right: 0; }
  .app[data-rail="1"] .nav a > span { display: none; }
  /* The heading goes; the GROUPING does not. A hairline keeps "Calls & Leads"
     apart from "Admin" once the words are gone, so the rail is still a map
     rather than eleven identical squares. */
  .app[data-rail="1"] .nav-group > .label { display: none; }
  .app[data-rail="1"] .nav-group { margin-bottom: var(--sp-2); padding-bottom: var(--sp-2); }
  .app[data-rail="1"] .nav-group + .nav-group {
    border-top: 1px solid var(--border); padding-top: var(--sp-2); }
  .app[data-rail="1"] .rail-toggle { justify-content: center; }
  .app[data-rail="1"] .rail-toggle > span { display: none; }
  .app[data-rail="1"] .rail-toggle svg { transform: rotate(0deg); }
}

/* Pinned to the bottom: .sidebar is a column flex, so auto margin pushes this
   below the nav however long the nav grows. */
.rail-toggle {
  margin-top: auto; display: flex; align-items: center; gap: 10px;
  padding: 9px var(--sp-3); border: 0; border-radius: var(--r-md);
  background: none; cursor: pointer; font: inherit;
  font-size: 13px; font-weight: 500; color: var(--ink-secondary);
  transition: background .12s, color .12s;
}
.rail-toggle:hover { background: rgba(31,35,40,.05); color: var(--ink); }
.rail-toggle svg { flex: 0 0 17px; width: 17px; height: 17px;
                   transform: rotate(180deg); transition: transform .18s ease; }
/* Below 960px the rail is a horizontal strip and there is nothing to collapse. */
@media (max-width: 960px) { .rail-toggle { display: none; } }

.main-col { display: flex; flex-direction: column; min-width: 0; }

.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: var(--sp-4);
  min-height: var(--topbar-h); padding: 0 var(--sp-6);
  background: rgba(246,248,251,.82);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.topbar .title { font-size: 15px; font-weight: 600; letter-spacing: -.01em; }
.topbar .spacer { flex: 1; }
.who { display: flex; align-items: center; gap: 9px; font-size: 13px; }
.who .avatar {
  width: 30px; height: 30px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--charcoal); color: #fff;
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
}
.who .name { font-weight: 600; color: var(--ink); line-height: 1.2; }
.role-tag {
  display: block; font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em; color: var(--ink-muted);
}

main { padding: var(--sp-6); max-width: 1360px; width: 100%; }

/* ---- buttons: solid fills, primary blue only ---- */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 15px; border: 1px solid transparent; border-radius: var(--r-md);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; white-space: nowrap;
  transition: background .12s, border-color .12s, box-shadow .12s, transform .06s;
}
.btn:active { transform: translateY(.5px); }
.btn-primary { background: var(--primary); color: #fff; box-shadow: 0 1px 2px rgba(9,105,218,.25); }
.btn-primary:hover { background: var(--primary-hover); }
.btn-charcoal { background: var(--charcoal); color: #fff; }
.btn-charcoal:hover { background: var(--charcoal-soft); }
.btn-ghost { background: var(--surface); color: var(--ink); border-color: var(--border);
             box-shadow: 0 1px 1px rgba(31,35,40,.03); }
.btn-ghost:hover { border-color: var(--border-strong); background: var(--canvas); }
.btn-sm { padding: 5px 11px; font-size: 12px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* ---- status pills: the ONLY place a status colour may appear ---- */
/* Fully rounded, a touch more air. The tint/foreground pairs are untouched -
   those are the measured numbers at the top of this file. */
.pill { border-radius: var(--r-full) !important; padding: 3px 11px !important; }
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px 2px 7px; border-radius: 999px;
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
.pill::before { font-size: 10px; line-height: 1; }   /* the mandatory icon */
/* The icons are a PROGRESSION, not decoration: an empty circle fills as the
   work moves, so the shape alone carries the state for anyone who cannot rely
   on the hue.   To do (empty) -> In progress (half) -> Review (three-quarter)
   -> Completed (tick).   The three exceptions are exceptions on purpose:
   Overdue warns, On hold pauses, Rework loops back.                         */
.pill-done     { background: var(--done-tint);     color: var(--done); }
.pill-done::before     { content: "\2713"; }        /* tick        */
.pill-duesoon  { background: var(--duesoon-tint);  color: var(--duesoon); }
.pill-duesoon::before  { content: "\25C6"; }        /* diamond     */
.pill-overdue  { background: var(--overdue-tint);  color: var(--overdue); }
.pill-overdue::before  { content: "\26A0"; }        /* warning     */
.pill-progress { background: var(--progress-tint); color: var(--progress); }
.pill-progress::before { content: "\25D0"; }        /* half circle */
.pill-review   { background: var(--review-tint);   color: var(--review); }
.pill-review::before   { content: "\25D5"; }        /* 3/4 circle  */
.pill-hold     { background: var(--hold-tint);     color: var(--hold); }
.pill-hold::before     { content: "\2016"; }        /* pause bars  */
.pill-rework   { background: var(--rework-tint);   color: var(--rework); }
.pill-rework::before   { content: "\21BA"; }        /* loop back   */
.pill-upcoming { background: var(--upcoming-tint); color: var(--upcoming); }
.pill-upcoming::before { content: "\25CB"; }        /* empty circle*/

/* ---- priority: OUTLINED pills ----------------------------------------
   Deliberately a different FORM from the filled status pills. Priority and
   state sit side by side on the same row, and both want red at the top end -
   "Urgent" must not read as "Overdue". Filled tint = state, outline = priority.
   'normal' renders NOTHING, so only a raised priority draws the eye.        */
.prio {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 8px; border-radius: 999px;
  background: var(--surface); border: 1.5px solid currentColor;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
}
.prio::before { font-size: 8px; }
.prio-urgent { color: var(--overdue); }
.prio-urgent::before { content: "\25B2\25B2"; }
.prio-high   { color: var(--duesoon); }
.prio-high::before   { content: "\25B2"; }
.prio-low    { color: var(--ink-muted); }
.prio-low::before    { content: "\25BC"; }

/* recurring marker - informational, never a status */
.recur { font-size: 11px; color: var(--ink-muted); white-space: nowrap; }
.recur::before { content: "\21BB\00A0"; }

/* shared-task marker: who else is on this job */
.shared { font-size: 11px; color: var(--ink-secondary); }
.shared::before { content: "\1F465\00A0"; }        /* two silhouettes */

/* an outstanding reassignment offer - a waiting state, not a status */
.pending-tag {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 1px 8px; border-radius: 999px;
  border: 1px dashed var(--review); color: var(--review);
  font-size: 11px; font-weight: 600; white-space: nowrap;
}
.pending-tag::before { content: "\21C4"; }         /* exchange arrows */

/* task timeline */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
  position: relative; padding: 0 0 10px 18px;
  border-left: 2px solid var(--border); margin-left: 4px;
  font-size: 12px; color: var(--ink-secondary);
}
.timeline li:last-child { border-left-color: transparent; padding-bottom: 0; }
.timeline li::before {
  content: ""; position: absolute; left: -5px; top: 4px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--border-strong);
}
.timeline li.now::before { border-color: var(--primary); background: var(--primary); }
.timeline b { color: var(--ink); font-weight: 600; }
.timeline time { color: var(--ink-muted); }

/* ---- radio group: selectable chips ------------------------------------
   Each option is a bordered chip rather than a bare radio on a line, so the
   set reads as one control with one answer. Three cues on the chosen one -
   the filled dot, the primary border and the tint - because a fill alone is
   colour-only and a dot alone is easy to miss at a glance.                */
.radios { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: 4px; }
.radios label {
  display: inline-flex; align-items: center; gap: 7px;
  margin: 0; padding: 7px 12px;
  border: 1px solid var(--border); border-radius: var(--r-md);
  font-size: 13px; font-weight: 500; color: var(--ink);
  cursor: pointer; white-space: nowrap; user-select: none;
}
.radios label:hover { border-color: var(--border-strong); background: var(--charcoal-tint); }
.radios label:focus-within { outline: 2px solid var(--primary); outline-offset: 2px; }
.radios input[type="radio"], .radios input[type="checkbox"] {
  flex: 0 0 auto; width: 15px; height: 15px; margin: 0;
}
.radios input:checked + span { font-weight: 600; }
/* :has() is the chip highlight; without it the dot and the bolded label still
   answer "which one is selected", so the control degrades rather than breaks. */
.radios label:has(input:checked) {
  border-color: var(--primary); background: var(--primary-tint);
}

/* ---- year calendar (Organisation) -------------------------------------
   A public holiday is NOT a task status, so it must not borrow a status
   colour: green, amber and red are spoken for, and reusing them here would
   make "office closed" read as "done" or "overdue". Charcoal carries it
   instead - the approved secondary - with a dot marker underneath so the day
   is never distinguished by colour alone. Primary blue still means "you are
   here", which is chrome, not status.                                     */
/* Twelve months capped to a tidy 6x2 block rather than spread across the full
   1400px. A year calendar is a reference you glance at, not the subject of the
   page - at 205px per month it was pushing the holiday list and the rule table
   off the bottom of the screen. */
.cal-year { display: grid; gap: var(--sp-3); max-width: 960px;
            grid-template-columns: repeat(auto-fill, minmax(144px, 1fr)); }
.cal-month { border: 1px solid var(--border); border-radius: var(--r-md); padding: 9px; }
.cal-cap { font-size: 10px; font-weight: 600; color: var(--ink-secondary);
           text-transform: uppercase; letter-spacing: .03em; margin-bottom: 4px; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; }
.cal-dow { font-size: 9px; color: var(--ink-muted); text-align: center; padding-bottom: 2px; }
.cal-day {
  position: relative; min-height: 17px; padding: 1px 0;
  display: flex; align-items: center; justify-content: center;
  font: inherit; font-size: 10px; font-variant-numeric: tabular-nums;
  border: 1px solid transparent; border-radius: 3px;
  background: none; color: var(--ink); cursor: pointer;
}
.cal-day:hover { border-color: var(--border-strong); }
/* The office calendar every role sees is drawn with spans, not buttons, because
   there is nothing to press. A pointer cursor and a hover border would promise
   a click that does nothing. */
span.cal-day { cursor: default; }
span.cal-day:hover { border-color: transparent; }
.cal-day.pad { visibility: hidden; cursor: default; }
.cal-day.off { color: var(--ink-muted); }                 /* nobody is rostered */
.cal-day.hol { background: var(--charcoal); color: #fff; font-weight: 700; }
.cal-day.hol::after {                                     /* the mandatory non-colour cue */
  content: ""; position: absolute; bottom: 1px; left: 50%; transform: translateX(-50%);
  width: 3px; height: 3px; border-radius: 50%; background: #fff;
}
.cal-day.today { box-shadow: inset 0 0 0 2px var(--primary); }
.cal-legend { display: flex; flex-wrap: wrap; gap: var(--sp-4);
              font-size: 12px; color: var(--ink-secondary); margin-top: var(--sp-4); }
.cal-legend span { display: inline-flex; align-items: center; gap: 6px; }
.cal-swatch { width: 15px; height: 15px; border-radius: var(--r-sm); border: 1px solid var(--border); }
.cal-swatch.hol { background: var(--charcoal); border-color: var(--charcoal); }
.cal-swatch.today { box-shadow: inset 0 0 0 2px var(--primary); }

/* ---- cards, tables, tiles ----------------------------------------------
   A card is WHITE ON THE CANVAS with a soft shadow and only a whisper of a
   border. The border is still there, at low contrast, because a shadow alone
   disappears in Windows high-contrast mode and in print - the card would
   dissolve into the page for exactly the readers who can least afford it.  */
.card {
  background: var(--surface);
  border: 1px solid rgba(216,220,225,.7); border-radius: var(--r-lg);
  padding: var(--sp-5); box-shadow: var(--shadow-card); margin-bottom: var(--sp-5);
}
.card > h2:first-child { margin-top: 0; }

.table-wrap { overflow-x: auto; margin: 0 calc(var(--sp-5) * -1) calc(var(--sp-5) * -1);
              padding: 0 var(--sp-5) var(--sp-2); }
.table { width: 100%; border-collapse: collapse; }
.table th {
  text-align: left; font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-muted);
  padding: 9px 14px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
/* Hairlines between rows, none under the last one. A closing rule directly
   above the card's own padding reads as a stray line rather than a boundary. */
.table td { padding: 13px 14px; border-bottom: 1px solid rgba(216,220,225,.65);
            vertical-align: top; }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--canvas); }
.table .empty { text-align: center; color: var(--ink-muted); padding: var(--sp-7); }

.tiles { display: grid; gap: var(--sp-4); margin-bottom: var(--sp-5);
         grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.tile {
  background: var(--surface);
  border: 1px solid rgba(216,220,225,.7); border-radius: var(--r-lg);
  padding: var(--sp-4) var(--sp-5); box-shadow: var(--shadow-card);
}
.tile .label { font-size: 12px; font-weight: 500; color: var(--ink-secondary); margin-bottom: 8px; }
/* Ink, never a series colour: the number is the fact, not a category. */
.tile .value { font-size: 30px; font-weight: 650; line-height: 1.05; letter-spacing: -.025em;
               font-variant-numeric: tabular-nums; }
.tile .delta { font-size: 12px; font-weight: 600; margin-top: 6px; }
.tile .delta.up   { color: var(--done); }
.tile .delta.down { color: var(--overdue); }
.tile .delta.up::before   { content: "\25B2 "; }
.tile .delta.down::before { content: "\25BC "; }

/* Tiles INSIDE a card are flat. The card already provides the white surface and
   the shadow; a bordered, shadowed tile drawn on top of it reads as a box in a
   box, which is what the scorecard's three metric groups looked like. */
.card .tiles .tile {
  background: none; border: 0; box-shadow: none;
  padding: 0 var(--sp-4) 0 0;
}
.card .tiles { gap: var(--sp-5) var(--sp-4); }
.card .tiles .tile .value { font-size: 24px; }

/* ---- stat tiles with an icon chip ---------------------------------------
   The chip is the only new colour on these cards and it invents none: each
   uses a fg/tint PAIR already measured in this file, every one >= 4.5:1 of
   its own tint. That is the number that matters here for the same reason it
   did for the pills - the icon sits ON the tint, not on white.

   NO BLUE. Rule 1 at the top of this file makes primary chrome-only, so a
   blue chip against a number would read as something to click. Where a count
   carries no status of its own the chip is charcoal, which says nothing. */
.tile-head { display: flex; align-items: center; justify-content: space-between;
             gap: var(--sp-3); margin-bottom: var(--sp-3); }
.tile-ico { display: grid; place-content: center; flex: 0 0 34px;
            width: 34px; height: 34px; border-radius: var(--r-md); }
.tile-ico svg { width: 17px; height: 17px; }
.tile-ico.neutral { background: var(--charcoal-tint); color: var(--ink-secondary); }
.tile-ico.good    { background: var(--done-tint);     color: var(--done); }
.tile-ico.warn    { background: var(--duesoon-tint);  color: var(--duesoon); }
.tile-ico.bad     { background: var(--overdue-tint);  color: var(--overdue); }
.card .tiles .tile .tile-head { margin-bottom: var(--sp-2); }

/* Side by side while there is room for both to be readable, stacked below. */
.dash-grid { display: grid; gap: var(--sp-4);
             grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

/* A rounded icon square at the head of a list row. Carries meaning at a glance
   without stealing the row's colour budget, which belongs to the status pill. */
.icon-tile {
  flex: 0 0 34px; width: 34px; height: 34px; border-radius: var(--r-md);
  display: grid; place-items: center;
  background: var(--canvas-deep); color: var(--ink-secondary);
  border: 1px solid rgba(216,220,225,.8);
}
.icon-tile svg { width: 16px; height: 16px; }

/* A section header inside a card: title left, controls right, one hairline. */
.card-head {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  margin: 0 0 var(--sp-4);
}
.card-head h2 { margin: 0; }
.card-head .spacer { flex: 1; }
.card-sub { font-size: 13px; color: var(--ink-secondary); margin: -8px 0 var(--sp-4); }

/* ---- page header --------------------------------------------------------
   Title and one line saying what the screen is for. Every view had an <h1>;
   about half also had a stray <p class="muted" style="margin-top:-6px">, which
   is why the spacing under a heading differed from screen to screen. */
.page-head { margin-bottom: var(--sp-5); max-width: 780px; }
.page-head h1 { margin-bottom: 6px; }
.page-head p { margin: 0; font-size: 14px; color: var(--ink-secondary); line-height: 1.55; }

/* ---- notice -------------------------------------------------------------
   Standing advice that is not a status and not a card: "these budgets started
   as estimates" is context for the whole screen, and dressing it as a card gave
   it the same weight as the data it is warning about. A left rule carries it
   instead - the same device an email quote uses. */
.notice {
  margin-bottom: var(--sp-5); padding: var(--sp-4) var(--sp-5);
  background: var(--surface); border: 1px solid rgba(216,220,225,.7);
  border-left: 3px solid var(--charcoal); border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
}
.notice h2 { font-size: 14px; margin: 0 0 6px; }
.notice p { margin: 0 0 8px; font-size: 13px; color: var(--ink-secondary); line-height: 1.55; }
.notice p:last-child { margin-bottom: 0; }
.notice-warn { border-left-color: var(--duesoon); }

/* ---- toolbar ------------------------------------------------------------
   Filters and view switches. NOT a card: a card is a container for content,
   and wrapping two dropdowns in one gave a filter row the same visual weight
   as the table it filters. This reads as chrome - inset, no shadow. */
.toolbar {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4);
  background: var(--canvas-deep); border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.toolbar > div { flex: 0 1 200px; }
.toolbar label { margin-top: 0; }
.toolbar .spacer { flex: 1 1 auto; }
.toolbar .note { flex: 1 1 100%; margin: 0; font-size: 12px; color: var(--ink-secondary); }
/* Inputs sit on white inside the tinted toolbar, so the control still reads as
   a control rather than melting into its own background. */
.toolbar input, .toolbar select { background: var(--surface); }

/* ---- form layout --------------------------------------------------------
   A form is read, not scanned, so it is capped rather than stretched across a
   1360px column. Nine fields on one line is not a form, it is a wall. */
.form-narrow { max-width: 760px; }
.form-grid {
  display: grid; gap: var(--sp-3) var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.form-grid > .wide { grid-column: 1 / -1; }
.form-grid label { margin-top: 0; }

/* The explanatory paragraph that belongs WITH a field rather than above the
   whole form. Quiet, indented, never bold. */
.field-note { font-size: 12px; color: var(--ink-muted); margin: 5px 0 0; }

/* ---- a card that folds shut -------------------------------------------
   For the secondary action on a page whose subject is something else. Native
   <details>, so the keyboard and screen-reader behaviour is the browser's and
   not a div pretending to be a button. */
.card-fold { padding: 0; }
.card-fold > summary {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: var(--sp-4) var(--sp-5);
  cursor: pointer; list-style: none; user-select: none;
  border-radius: var(--r-lg);
}
.card-fold > summary::-webkit-details-marker { display: none; }
/* The chevron is drawn here rather than left to the browser's default triangle,
   which cannot be positioned and looks nothing like the rest of the app. */
.card-fold > summary::before {
  content: ""; flex: 0 0 8px; width: 8px; height: 8px; margin-right: 2px;
  border-right: 1.8px solid var(--ink-muted); border-bottom: 1.8px solid var(--ink-muted);
  transform: rotate(-45deg) translate(-1px, -1px); transition: transform .15s;
}
.card-fold[open] > summary::before { transform: rotate(45deg) translate(-2px, -2px); }
.card-fold > summary:hover { background: var(--canvas); }
.card-fold > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.card-fold .sum-title { font-size: 15px; font-weight: 600; letter-spacing: -.01em; }
.card-fold .sum-hint { font-size: 12.5px; color: var(--ink-muted); }
.card-fold > *:not(summary) { padding-left: var(--sp-5); padding-right: var(--sp-5); }
.card-fold > *:last-child { padding-bottom: var(--sp-5); }

/* ---- a person in a table row ----
   Initials tile, name, and what they do stacked underneath - so one column
   carries what previously needed two, and the row has an anchor for the eye. */
.person { display: flex; align-items: center; gap: 10px; }
.person-av {
  flex: 0 0 32px; width: 32px; height: 32px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--charcoal-tint); color: var(--charcoal-soft);
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
}
.person-name { font-weight: 600; display: block; }
.person-sub { display: block; font-size: 12px; color: var(--ink-muted); }

/* A count that is zero is a dash, and a dash is deliberately faint: five
   columns of noughts is five things to read and discard before finding the one
   number that matters. */
.board-n { font-variant-numeric: tabular-nums; font-size: 15px; }
.board-nil { color: var(--border-strong); font-variant-numeric: tabular-nums; }

/* Row actions: links, spaced, no hand-typed middot separators. */
.row-acts { white-space: nowrap; }
.row-acts a + a { margin-left: var(--sp-3); }

/* A group of small buttons in a table cell. Left to inline-block they wrapped
   two-then-one-then-one down the column; as a flex row they break evenly and
   the pairs stay together. */
.btn-cluster { display: flex; flex-wrap: wrap; gap: 6px; }

/* A date must never break mid-value: "2026-08-" on one line and "06" on the
   next is not a date, it is two numbers. */
.nowrap { white-space: nowrap; }

/* ---- forms ----
   The text-field reset EXCLUDES radios and checkboxes. It used to hit every
   input and be walked back by `.radios input { width: auto }` further down -
   which meant a stale or unloaded stylesheet rendered a radio as a
   full-width box with the dot floating in the middle of it. A control that
   only looks right when a second rule also arrives is not styled, it is
   balanced.                                                                */
label { display: block; font-size: 12px; font-weight: 600;
        color: var(--ink-secondary); margin: var(--sp-3) 0 5px; }
input:not([type="radio"]):not([type="checkbox"]), select, textarea {
  width: 100%; padding: 9px 11px; font: inherit; font-size: 13px;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-md);
  transition: border-color .12s, box-shadow .12s;
}
input::placeholder, textarea::placeholder { color: var(--ink-muted); }
input:not([type="radio"]):not([type="checkbox"]):focus,
select:focus, textarea:focus {
  border-color: var(--primary); outline: none; box-shadow: 0 0 0 3px var(--primary-ring);
}
input[type="radio"], input[type="checkbox"] {
  width: auto; accent-color: var(--primary); cursor: pointer;
}
.row { display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: flex-end; }
.row > * { flex: 1 1 160px; }
.row > .btn { flex: 0 0 auto; }

/* ---- toast ---- */
#toast {
  position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
  background: var(--charcoal); color: #fff; padding: 11px 18px;
  border-radius: var(--r-md); font-size: 13px; font-weight: 500;
  box-shadow: var(--shadow-pop); max-width: min(560px, calc(100vw - 32px));
  opacity: 0; pointer-events: none; transition: opacity .18s, transform .18s;
  z-index: 100;
}
#toast.show { opacity: 1; }

/* ---- responsive ---------------------------------------------------------
   Below 960px the rail becomes a horizontal strip along the top rather than
   disappearing behind a hamburger. A menu you have to open is a menu people
   stop using, and this app's whole point is that the next screen is one click
   away.                                                                    */
@media (max-width: 960px) {
  .app { grid-template-columns: 1fr; }
  .sidebar {
    position: static; height: auto; flex-direction: row; align-items: center;
    gap: var(--sp-3); overflow-x: auto; padding: var(--sp-2) var(--sp-4);
    border-right: 0; border-bottom: 1px solid var(--border);
  }
  .sidebar .brand { padding: 0 var(--sp-2) 0 0; }
  .sidebar .nav { display: flex; gap: var(--sp-1); }
  .nav-group { display: flex; align-items: center; gap: var(--sp-1); margin: 0; }
  .nav-group > .label { display: none; }
  .nav a { white-space: nowrap; margin: 0; }
  .topbar { padding: 0 var(--sp-4); }
  main { padding: var(--sp-4); }
}

@media (max-width: 640px) {
  h1 { font-size: 21px; margin-bottom: var(--sp-4); }
  .card { padding: var(--sp-4); }
  .table-wrap { margin: 0 calc(var(--sp-4) * -1) calc(var(--sp-4) * -1);
                padding: 0 var(--sp-4) var(--sp-2); }
  .nav a span { display: none; }        /* icons only, once names stop fitting */
  .nav a { padding: 8px 10px; }
}

/* Nothing in this nav navigates AWAY from the app - every destination is a view
   in the table in index.html, the 3CX notifier's five included. The outbound
   arrow that used to mark the links to /admin went with them. */

/* ---- lead settings ------------------------------------------------------
   The department tickboxes are the SUBJECT of that screen, not a field on a
   form: ticking one decides who can see a whole business line's customers. So
   they get room to be read and clicked, and wrap rather than scroll. */
/* The "nobody set" warning under Lead manager. OUT OF FLOW, with .ls-fields
   reserving the strip it lands in, so the card does not change height when a
   manager is chosen or cleared - which it did, jumping the row every time. */
.ls-fields { padding-bottom: 34px; }
.ls-warn {
  position: absolute; top: 100%; left: 0; right: 0; margin: 5px 0 0;
  font-size: 12px; line-height: 1.35; font-weight: 600; color: var(--duesoon);
}
@media (max-width: 720px) {
  /* Stacked, the columns are full width and an absolute strip would land on top
     of the next field. Back into flow, where there is room for it. */
  .ls-fields { padding-bottom: 0; }
  .ls-warn { position: static; }
}

/* ---- adding a business line ---------------------------------------------
   .row bottom-aligns, which is right while every column is label + input: the
   inputs line up because each is the last thing in its column. Put a field-note
   under ONE of them and that column's bottom becomes the note's bottom, so its
   neighbour's input drops to meet it and the two sit at visibly different
   heights - which is exactly what happened here.

   Top-align instead. Every label is one line, so the inputs line up from above
   and the note simply hangs below the column it belongs to. */
.ls-addline { align-items: flex-start; }

.ls-depts {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4);
  padding: var(--sp-3) var(--sp-4); margin-top: 4px;
  background: var(--canvas-deep); border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.ls-dept, .ls-active {
  display: inline-flex; align-items: center; gap: 7px;
  margin: 0; font-size: 13px; font-weight: 500; cursor: pointer;
}
.ls-dept input, .ls-active input { width: auto; margin: 0; }
.ls-line + .ls-line { margin-top: var(--sp-4); }

/* ---- leads: the timeline -------------------------------------------------
   The history is the point of the detail page, so it gets the room. A rail
   down the left ties the entries into one sequence rather than a stack of
   cards, which is what makes it read as a story. */
.lv-time { list-style: none; margin: 0; padding: 0; }
.lv-time li {
  display: flex; gap: var(--sp-3);
  padding: 0 0 var(--sp-4) var(--sp-2);
  border-left: 2px solid var(--border); margin-left: 14px;
}
.lv-time li:last-child { border-left-color: transparent; padding-bottom: 0; }
.lv-ic {
  flex: 0 0 28px; width: 28px; height: 28px; margin-left: -16px;
  border-radius: var(--r-full); display: grid; place-items: center;
  background: var(--surface); border: 1px solid var(--border);
  font-size: 12px; color: var(--ink-secondary);
}
.lv-body { min-width: 0; flex: 1; padding-top: 3px; }
.lv-head {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 13px;
}
.lv-head .spacer { flex: 1; }
.lv-body p { margin: 4px 0 0; font-size: 13px; color: var(--ink-secondary); white-space: pre-wrap; }

.lv-fields { display: grid; gap: var(--sp-3) var(--sp-5);
             grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.lv-f { font-size: 13px; }
.lv-f .label {
  display: block; font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-muted); margin-bottom: 2px;
}
.lv-note { padding: var(--sp-3) 0; border-bottom: 1px solid var(--border); }
.lv-note:last-child { border-bottom: 0; }
.lv-note p { margin: 0 0 4px; font-size: 13px; white-space: pre-wrap; }
